#install.packages("knitr")
#install.packages("grid")
#install.packages("MLRMPA")
#install.packages("dprep")
#install.packages("normalr")
#install.packages("ggcorrplot")
#install.packages("RColorBrewer")
#install.packages("rgdal")
#install.packages("jsonlite")
#install.packages("RColorBrewer")
#install.packages("readr")
Sys.setlocale("LC_ALL", "English")
Sys.setenv("LANGUAGE"="En")
library(gridExtra)
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
library(normalr)
library(ggcorrplot)
library(leaflet)
library(plotly)
library(RColorBrewer)
library(readr)
#??src_mysql
my_db <- src_mysql(
dbname = "covid",
host = "localhost",
user = "root",
password = "1234"
)
my_db
##import data
df_conf <- tbl(my_db, sql("select * from covid19_confirmed"))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from covid19_deaths"))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from covid19_recovered"))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
cleanData <- function(data) {
## remove some columns
data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
## convert from wide to long format
data %<>% gather(key=date, value=count, -country)
## convert from character to date
data %<>% mutate(date = date %>% mdy())
## aggregate by country
data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data
data.world <- data %>% group_by(date) %>%
summarise(country='World',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T),
recovered = sum(recovered, na.rm=T))
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
View(data)
x <- raw.confirmed
x$confirmed <- x[, ncol(x)]
x %<>% select(c(Country.Region, Province.State, Lat, Long, confirmed)) %>%
mutate(txt=paste0(Country.Region, ' - ', Province.State, ': ', confirmed))
m <- leaflet(width=1200, height=800) %>% addTiles()
# circle marker (units in pixels)
m %<>% addCircleMarkers(x$Long, x$Lat,
# radius=2+log2(x$confirmed),
radius=0.03*sqrt(x$confirmed),
stroke=F,
color='red', fillOpacity=0.3,
label=x$txt)
# world
m
## China
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
7: Unknown or uninitialised column: `Country`.
8: Unknown or uninitialised column: `Country`.
9: Unknown or uninitialised column: `Country`.
m %>% setView(95, 35, zoom=4)
## Australia and New Zealand
m %>% setView(135, -27, zoom=4)
## US and Canada
m %>% setView(-105, 40, zoom=4)
## Europe
m %>% setView(10, 50, zoom=4)
NA
# new.confirmed new.deaths new.recovered
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1),
rate.upper = ifelse(is.nan(rate.upper), 0, rate.upper))
## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1),
rate.lower = ifelse(is.nan(rate.lower), 0, rate.lower))
## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1),
rate.daily = ifelse(is.nan(rate.daily), 0, rate.daily))
View(data)
## convert from wide to long format
Warning message:
Unknown or uninitialised column: `Country`.
data.long <- data %>%
select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
current.confirmed='Current Confirmed',
recovered='Recovered',
deaths='Deaths'))
View(data.long)
df <- data.long %>% filter(country %in% top.countries) %<>%
There were 14 warnings (use warnings() to see them)
mutate(country=country %>% factor(levels=c(top.countries)))
df %>% filter(country != 'World' & type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count, color=type)) +
geom_line()+
#geom_area(alpha=0.5) +
# xlab('') + ylab('') +
labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries - ',
max.date.txt)) +
scale_color_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=12),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.4, 'cm'),
legend.text=element_text(size=12),
strip.text.x=element_text(size=12),
axis.text=element_text(size=12),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~country, ncol=4, scales='free_y') +
scale_y_continuous(trans='log10')

##Number of case World
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
7: Unknown or uninitialised column: `Country`.
8: Unknown or uninitialised column: `Country`.
world <- filter(data.long,country == 'World')
world
plot1 <- world %>% filter(type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count)) +
geom_area(aes(fill=type), alpha=0.5) +
labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
scale_fill_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1))
plot2 <- world %>%
ggplot(aes(x=date, y=count)) +
geom_line(aes(color=type)) +
labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1)) +
scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

gly.plot1 <- ggplotly(plot1)
gly.plot1
gly.plot2 <- ggplotly(plot2)
gly.plot2
## Current Confirmed Cases
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
data.world <- data %>% filter(country=='World')
n <- nrow(data.world)
plot1 <- ggplot(data.world, aes(x=date, y=current.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

View(data.world)
## a scatter plot with a smoothed line and vertical x-axis labels
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
plot1 <- ggplot(data.world, aes(x=date, y=deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.world, aes(x=date, y=new.deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.world, aes(x=date, y=new.recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

## convert from wide to long format, for drawing area plots
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
rates.long <- data %>%
select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
rate.upper='Upper bound',
rate.lower = 'Lower bound'))
#View(rates.long)
g <- rates.long %>% filter(country == "World") %>%
ggplot(aes(x = date, y = count, color = type)) +
geom_line() +
labs(title = "World Death Rate (%)") +
xlab("") + ylab("Death Rate (%)")
g

gly.death <- ggplotly(g)
gly.death
## ranking by confirmed cases
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
data.latest.all <- data %>% filter(date == max(date)) %>%
select(country, date,confirmed, new.confirmed, current.confirmed,
recovered, deaths, new.deaths, death.rate=rate.lower) %>%
mutate(ranking = dense_rank(desc(confirmed))) %>%
arrange(ranking)
View(data.latest.all)
k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
[1] "US" "India" "Brazil" "Russia" "United Kingdom" "France" "Spain" "Italy"
[9] "Turkey" "Germany" "Colombia" "Argentina" "Mexico" "Poland" "South Africa" "Iran"
[17] "Ukraine" "Peru" "Indonesia" "Netherlands"
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
There were 11 warnings (use warnings() to see them)
mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
current.confirmed=sum(current.confirmed),
recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
mutate(death.rate=(100 * deaths/confirmed) %>% round(1))
`summarise()` ungrouping output (override with `.groups` argument)
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths, current.confirmed,recovered)) %>%
mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest
df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")
# Add World Population
world_pop <- sum(df_pop$`Population (2020)`)
df_pop[nrow(df_pop) + 1,] = c("World", world_pop)
# Add Other Countries Population
top_pop <- filter(df_pop, df_pop$country %in% top.countries & df_pop$country != "World")
top_pop <- sum(top_pop$`Population (2020)` %>% as.numeric())
others_pop <- (world_pop - top_pop)
df_pop[nrow(df_pop) + 1,] = c("Others", others_pop)
View(df_pop)
data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE)
data.latest
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest$population <- data.latest$population %>% as.numeric()
data.latest <- data.latest %>%
select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths,
current.confirmed, recovered, recover.rate, population)) %>%
mutate(confirm.rate = (100 * confirmed / population) %>% round(1))
data.latest
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
## convert from wide to long format, for drawing area plots
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
data.latest.long <- data.latest %>% filter(country!='World') %>%
gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
confirmed='Total Confirmed',
deaths='Total Deaths',
death.rate='Death Rate (%)',
new.confirmed='New Confirmed (compared with one day before)',
new.deaths='New Deaths (compared with one day before)',
current.confirmed='Current Confirmed',
recover.rate = 'Recovered Rate(%)',
confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
| type=='Total Deaths'
| type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Death Rate (%)'
# | type=='New Confirmed (compared with one day before)'
# | type=='New Deaths (compared with one day before)'
| type=='Recovered Rate(%)'
| type=='Confirmed Rate(%)')
data.two.dem
## bar chart
Warning message:
Unknown or uninitialised column: `Country`.
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=3, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=8),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')

data.two.dem$facet <- factor(data.two.dem$type, levels = c('Confirmed Rate(%)', 'Recovered Rate(%)','Death Rate (%)'))
Warning message:
Unknown or uninitialised column: `Country`.
data.two.dem %>%
ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=4, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=10),
axis.text.x=element_text(size=10,angle=45, hjust=1)) +
facet_wrap(~facet, ncol=1, scales='free_y')

##GDP
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
df_gdp <- tbl(my_db, sql("select * from gdp"))
df_gdp <- as.data.frame(df_gdp)
df_gdp <- rename(df_gdp,"country"="Real GDP growth (Annual percent change)")
df_gdp <- select(df_gdp,c("country","2012","2013","2014","2015","2016","2017","2018","2019","2020","2021"))
df_gdp
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
NA
#healthranking
Warning message:
Unknown or uninitialised column: `Country`.
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
View(df_healt)
#Top20Pornhub
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
df_pornhub <- tbl(my_db, sql("select * from Pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
#temp
df_temp <- tbl(my_db, sql("select * from world_temp"))
df_temp <- as.data.frame(df_temp)
df_temp$Country[df_temp$Country == "United States"] <- "US"
df_city <- select(df_temp,c("Country","City")) %>%
rename(country=Country) %>%
rename(city=City)
numofcity <- aggregate(city ~ country, data = df_city, length)
df_temp <- select(df_temp,c("Country","Avg_Year")) %>%
rename(country=Country)
View(df_temp)
#df_temp <- data.frame(country=df_temp[,1],avg=rowMeans(df_temp[,-1]))
df_temp <- df_temp %<>% group_by(country) %>% summarise(avg_temp = mean(Avg_Year,na.rm = TRUE)%>% round(1))
`summarise()` ungrouping output (override with `.groups` argument)
df_temp
#display.brewer.all()
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
df_temp.all <- df_temp %>% merge(data.latest.all)
View(df_temp.all)
df_temp_top.all <- df_temp.all %>% filter(country %in% top.countries) %>%
mutate(ranking = ranking - 1) %>%
arrange(ranking)
View(df_temp_top)
g_temp_top <- df_temp_top %>%
ggplot(aes(x = reorder(country, ranking), y = avg_temp, fill = avg_temp)) +
labs(title=paste0("Temperature in Top 20 countries"), subtitle = "Average Temperature in Top 20 countries with most confirmed cases (°C) (2020)") +
scale_color_gradient(low = "#93DBFF", high = "#FF7771") +
geom_text(aes(label=avg_temp, y=avg_temp), size=4, vjust=-0.5) +
geom_bar(stat = "identity", position = "dodge") +
theme(
legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size = 15, hjust = 0.5),
plot.subtitle = element_text(size = 12, hjust = 0.5),
axis.text=element_text(size=8),
axis.text.x=element_text(size = 9, angle=45, hjust=1)) +
scale_x_discrete(name = "Country") +
scale_y_discrete(name = "Average Temperature")
#labs(title = "Temperature in Top 20 countries", subtitle = "Temperature in Top 20 countries with most confirmed cases (°C)")
#g_temp_top
g_temp_top

#df_conf
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
7: Unknown or uninitialised column: `Country`.
8: Unknown or uninitialised column: `Country`.
9: Unknown or uninitialised column: `Country`.
#data.latest.all
lat.long <- rename(df_conf, "country" = "Country.Region", "city" = "Province.State") %>%
select("country", "Lat", "Long") %>%
merge(df_temp.all[c("country","confirmed", "recovered", "deaths", "avg_temp", "ranking")], by = "country") %>%
distinct(country, .keep_all = TRUE) %>%
mutate(ranking = ranking - 1) %>%
arrange(ranking)
View(lat.long)
label_world <- lat.long
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
label_world$avg_temp <- as.numeric(label_world[, names(label_world) %in% c("avg_temp")])
label_world <- label_world %>%
mutate(txt=paste0('<b>',ranking, '</b>',
'<br/>','<b>',country, '</b>',
'<br/>', "Temperature: ",avg_temp, ' °C',
'<br/>', "Confirmed: ", confirmed,
'<br/>', "Deaths: ", deaths,
'<br/>', "Recovered: ", recovered
))
label_world$txt <- label_world$txt %>% lapply(htmltools::HTML)
label_world
label_top <- label_world %>% filter(ranking < 21)
label_top
wpal <- colorNumeric("YlOrRd", label_world$avg_temp, n = 4)
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
topIcon <- makeIcon("star.png",
#iconUrl = "https://static.vecteezy.com/system/resources/previews/001/189/063/non_2x/star-rounded-png.png",
iconWidth = 10, iconHeight = 10
#iconAnchorX = 20, iconAnchorY = 20
)
label_world <- label_world %>% filter(ranking > 20)
m <- leaflet(width=1200, height=800) %>% addTiles()
m %<>% addCircleMarkers(label_world$Long, label_world$Lat,
# radius=2+log2(x$confirmed),
radius=10,#*log2(m.world$avg.temp),
stroke=F,
#color='red',
color = wpal(label_world$avg_temp),
fillOpacity=0.5,
#popup=label.top$txt
label= label_world$txt,
group = "World"
) %>%
addCircleMarkers(label_top$Long, label_top$Lat,
# radius=2+log2(x$confirmed),
radius=10,#*log2(m.world$avg.temp),
stroke=F,
#color='red',
color = wpal(label_top$avg_temp),
fillOpacity=0.5,
#popup=label.top$txt
label= label_top$txt,
group = "Top 20 Countries"
) %>%
addLabelOnlyMarkers(label_top$Long, label_top$Lat, label = label_top$ranking,
labelOptions = labelOptions(noHide = TRUE, textOnly = TRUE,
direction = "head",
offset = c(5,4)),
group = "Top 20 Countries") %>%
addLegend("bottomright", pal = wpal, values = label_world$avg_temp, opacity = 1,
labFormat = labelFormat(suffix = " °C"),
title = "Temperature") %>%
addLayersControl(
#baseGroups = c("OSM (default)", "Toner", "Toner Lite"),
overlayGroups = c("Top 20 Countries", "World"),
options = layersControlOptions(collapsed = FALSE)
)
m
NA
#Top 20 with gdp
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(country!='World'& country!='Others')
#data.top <- head(data.top,20)
View(data.top)
data.gdp <- filter(data.longGDP,year=='2020')
View(data.gdp)
#rank GDP
Warning message:
Unknown or uninitialised column: `Country`.
data.top.hight <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(desc(GDP)))
#data.top.hight
k <- 20
top.gdp <- data.top.hight %>%
filter(ranking < k) %>%
arrange(ranking)
data.top.low <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(GDP))
low.gdp.long <- data.top.low %>%
filter(ranking < k) %>%
arrange(ranking)
View(low.gdp.long)
top.gdp
low.gdp
world.gdp <- df_gdp %>% filter(country == "World") %>%
gather(key = Year, value = GDP, -country)
world.gdp
world.gdp$group <- cut(world.gdp$GDP, c(-Inf,0, Inf), labels = c("-","+"))
g <- ggplot(world.gdp, aes(x = Year, y = GDP, fill = group)) +
geom_text(aes(label=GDP, y = GDP), size=3, vjust=-1) +
labs(title=paste0("Real GDP growth (Annual percent change) of World from 2012-2021"))+
geom_bar(stat = "identity")+
scale_fill_manual(values = c("-" = "red","+" = "limegreen"))+
theme_bw() + theme(legend.position = "none")+
xlab("")+
theme(#legend.title=element_blank(),
plot.title = element_text(size=7),
axis.text=element_text(size=8))
g
gly.world.gdp <- ggplotly(g)
gly.world.gdp
gdp.top20 <- df_gdp2019 %>%
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
select(c("rank", "country", "GDP (millions of US dollars)")) %>%
merge(data.latest.all %>%
select(country, ranking, confirmed, recovered, deaths) %>%
filter(country %in% top.countries & country != "World"), by = "country") %>%
arrange(ranking) %>%
mutate(ranking = ranking - 1)
gdp.top20 %<>% rename("GDP" = "GDP (millions of US dollars)")
gdp.top20
g <- ggplot(gdp.top20, aes(x = GDP, y = reorder(country, -ranking))) +
geom_histogram(stat = "identity", aes(fill = GDP))+
scale_fill_gradient("GDP", low = "#FF4038", high = "#50E952") +
labs(title=paste0("GDP of Top 20 Countries in 2019 (millions of US dollars)")) +
geom_text(aes(label=GDP, x = GDP), size=3.5, hjust=-0.2) +
xlab("GDP (millions of US dollars)") +
ylab("") +
theme(legend.title=element_blank())
Ignoring unknown parameters: binwidth, bins, pad
g

gly.top.gdp <- ggplotly(g)
gly.top.gdp
df_sars <- tbl(my_db, sql("select * from sars_2003"))
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
df_sars <- as.data.frame(df_sars)
df_sars
dates.s <- df_sars[,1]%>% mdy()
range(dates.s)
[1] "2003-03-17" "2003-07-11"
min.date.s <- min(dates.s)
max.date.s <- max(dates.s)
min.date.txt.s <- min.date.s %>% format('%d %b %Y')
max.date.txt.s <- max.date.s %>% format('%d %b %Y')
# clean data Sars
data.sars <- df_sars %>% rename(c("date" = "Date", "confirmed" = "Cumulative_number" ,"deaths" = "Number_deaths", "recovered" = "Number_recovered")) %>%
mutate(date = date %>% mdy()) %>%
group_by(country, date) %>% as.data.frame()
View(data.sars)
# Add World's Sars cases
world.sars <- data.sars %>% group_by(date) %>%
summarise(country='World',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T),
recovered = sum(recovered, na.rm=T))
data.sars %<>% rbind(world.sars)
data.sars %<>% mutate(current.confirmed = confirmed - deaths - recovered)
#View(world.sars)
#View(data.sars)
#rate
data.sars %<>% arrange(country, date)
n <- nrow(data.sars)
day1.sars <- min(data.sars$date)
data.sars %<>% mutate(new.confirmed = ifelse(date == day1.sars, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1.sars, NA, deaths - lag(deaths, n=1)),
new.recovered = ifelse(date == day1.sars, NA, recovered - lag(recovered, n=1)))
data.sars %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data.sars %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1),
rate.upper = ifelse(is.nan(rate.upper), 0, rate.upper))
## lower bound: death rate based on total confirmed cases
data.sars %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1),
rate.lower = ifelse(is.nan(rate.lower), 0, rate.lower))
## death rate based on the number of death/recovered on every single day
data.sars %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1),
rate.daily = ifelse(is.nan(rate.daily), 0, rate.daily))
View(data.sars)
## convert from wide to long format
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
data.sars.long <- data.sars %>%
select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.sars.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
current.confirmed='Current Confirmed',
recovered='Recovered',
deaths='Deaths'))
View(data.sars.long)
# World sars' long data
world.sars.long <- data.sars.long %>%
filter(country == "World")
View(world.sars.long)
g <- ggplot(world.sars.long, aes(date, count, color = type)) +
geom_line()+
labs(title = "Number of Cases Worldwide: SARs")+
xlab("")+
ylab("")
g

gly.g <- ggplotly(g)
gly.g
gly.plot2
## Current Confirmed Cases
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
data.sars.world <- data.sars %>% filter(country=='World')
View(data.sars.world)
n <- nrow(data.sars.world)
View(data.sars.world)
plot1 <- ggplot(data.sars.world, aes(x=date, y=current.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

## a scatter plot with a smoothed line and vertical x-axis labels
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
plot1 <- ggplot(data.sars.world, aes(x=date, y=deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.sars.world, aes(x=date, y=recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.sars.world, aes(x=date, y=new.deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.sars.world, aes(x=date, y=new.recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

#df_thai <- tbl(my_db, sql("select * from covid_thailand_updated"))
#df_thai <- as.data.frame(df_thai)
df_thai <- read_csv("D:/4D-2/Project 2/Data/covid_thailand_updated.csv")
Sys.setlocale("LC_CTYPE", "Thai")
options(encoding = "UTF-8")
#View(df_thai)
dates.th <- df_thai[,2]%>% mdy()
range(dates.th)
min.date.th <- min(dates.th)
max.date.th <- max(dates.th)
min.date.txt.th <- min.date.th %>% format('%d %b %Y')
max.date.txt.th <- max.date.th %>% format('%d %b %Y')
df_thai$announce_date <- mdy(df_thai$announce_date)
df_thai$notification_date <- mdy(df_thai$notification_date)
df_thai
df_thai <- df_thai %>% select(!No.) %>%
group_by(announce_date)
df_thai
# Total confirmed cases in Thailand
data.thai.count <- df_thai %>%
select(announce_date) %>%
summarise(comfirmed = n()) %>% as.data.frame()
data.thai.count
data.thai.count$cumulative_confirmed <- cumsum(data.thai.count[, 2])
data.thai.count
g.th <- ggplot(data.thai.count) +
geom_line(aes(x = announce_date, y = cumulative_confirmed)) +
labs(title = "Thai Confirmed Cases (Jan 2020 - Jan 2021)") +
xlab("Date (Announce Date)") +
ylab("Confirmed Cases")
g.th
gly.th <- ggplotly(g.th)
gly.th
# Confirmed cases divided by sex (gender)
data.thai.gender <- df_thai %>%
group_by(sex) %>%
summarise(count = n()) %>%
mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
#mutate(pos = cumsum(percent) - 0.5*percent) %>%
arrange(desc(percent))
data.thai.gender
data.thai.gender$sex <- factor(data.thai.gender$sex, levels = as.character(data.thai.gender$sex))
data.thai.gender$sex
g.th.gender <- data.thai.gender %>%
ggplot(aes(x = "", y = percent, fill = sex)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y") +
theme_void() +
geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
guides(fill = guide_legend(reverse = TRUE))
g.th.gender
# Confirmed cases divided by age
data.thai.age <- df_thai %>%
group_by(age) %>%
summarise(count = n()) %>%
arrange(desc(count))
data.thai.age
# Confirmed cases divided by nationality
data.thai.nationality <- df_thai %>%
group_by(nationality) %>%
summarise(count = n()) %>%
arrange(desc(count))
data.thai.nationality
# Data in US
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
df_us <- tbl(my_db, sql("select * from covidus"))
df_us <- as.data.frame(df_us)
df_us_pop <- tbl(my_db, sql("select * from data_population"))
df_us_pop <- as.data.frame(df_us_pop)
df_us_gender <- tbl(my_db, sql("select * from data_gender"))
df_us_gender <- as.data.frame(df_us_gender)
df_us_ethnic <- tbl(my_db, sql("select * from data_ethnic"))
df_us_ethnic <- as.data.frame(df_us_ethnic)
df_us_lockdown <- tbl(my_db, sql("select * from data_lockdown"))
df_us_lockdown <- as.data.frame(df_us_lockdown)
df_us_health <- tbl(my_db, sql("select * from data_health"))
df_us_health <- as.data.frame(df_us_health)
df_us_testing <- tbl(my_db, sql("select * from data_testing"))
df_us_testing <- as.data.frame(df_us_testing)
df_us_latlong <- tbl(my_db, sql("select * from us_latlong"))
df_us_latlong <- as.data.frame(df_us_latlong)
#View(df_us_latlong)
data.us <- df_us %>% select(date, state, cases, deaths) %>%
mutate(date = date %>% mdy()) %>%
rename("confirmed" = "cases")
#data.us
dates.us <- data.us[,1]
range(dates.us)
[1] "2020-01-21" "2020-12-24"
min.date.us <- min(dates.us)
max.date.us <- max(dates.us)
min.date.txt.us <- min.date.us %>% format('%d %b %Y')
max.date.txt.us <- max.date.us %>% format('%d %b %Y')
day1.us <- min(data.us$date)
data.us.total <- data.us %>% group_by(date) %>%
summarise(state='US',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
#View(data.us.total)
data.us %<>% rbind(data.us.total)
View(data.us)
data.us.long <- data.us %>%
gather(key = type, value = count, -c(date, state))
#data.us.long
us.total <- data.us.total %>%
mutate(new.confirmed = ifelse(date == day1, 0, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, 0, deaths - lag(deaths, n=1)))
us.total %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths))
us.total
us.total.long <- us.total %>%
gather(key = type, value = count, -c(date, state))
View(us.total.long)
g1 <- data.us.long %>% filter(state == "US") %>%
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
ggplot(aes(x = date, y = count)) +
geom_area(aes(fill=type), alpha=0.5) +
labs(title = paste0("Cumulative cases in US : ", min.date.txt.us, '-', max.date.txt.us, " (Log Scale)")) +
scale_y_continuous(trans='log10')+
scale_fill_manual(values=c('red', 'black'))+
ylab("")
g2 <- us.total.long %>%
filter(type %in% c("new.confirmed")) %>%
ggplot(aes(x = date, y = count, color = type)) +
geom_line() +
labs(title = paste0("Daily confirmed cases in US : ", min.date.txt.us, '-', max.date.txt.us)) +
xlab("Date") +
ylab("Confirmed cases")
gly.us1 <- ggplotly(g1)
Transformation introduced infinite values in continuous y-axis
gly.us2 <- ggplotly(g2)
gly.us1
gly.us2
NA
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
data.us %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)))
data.us %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths))
View(data.us)
#data.us.daily <- data.us %>% filter(state == "US")
#View(data.us.daily)
data.us.pop <- df_us_pop %>% select(State, Population) %>%
rename("state" = "State")
#data.us.pop
data.us.gender <- df_us_gender %>% select(State, Male, Female) %>%
rename("state" = "State")
data.us.gender
data.us.latest <- data.us %>%
filter(date == max.date.us) %>%
merge(data.us.pop, by = "state", all.x = T) %>%
merge(data.us.gender, by = "state", all.x = T)
#View(data.us.latest)
data.us.latest$Population[data.us.latest$state == "US"] <- sum(data.us.pop$Population)
data.us.latest %<>% mutate(ranking = dense_rank(desc(confirmed)),
confirmed.rate = (100 * confirmed / Population) %>% round(2),
death.rate = (100 * deaths / confirmed) %>% round(2),
Male.confirmed = (Male * confirmed) %>% round(0),
Female.confirmed = Female * confirmed %>% round(0),
Male.deaths = Male * deaths,
Female.deaths = Female * deaths) %>%
arrange(ranking)
top.us <- data.us.latest[,1]
top.us
[1] "US" "California" "Texas" "Florida" "Illinois"
[6] "New York" "Ohio" "Georgia" "Pennsylvania" "Tennessee"
[11] "Michigan" "Wisconsin" "North Carolina" "Indiana" "Arizona"
[16] "New Jersey" "Minnesota" "Missouri" "Massachusetts" "Alabama"
[21] "Virginia" "Colorado" "Louisiana" "South Carolina" "Iowa"
[26] "Oklahoma" "Maryland" "Utah" "Kentucky" "Washington"
[31] "Kansas" "Nevada" "Arkansas" "Mississippi" "Connecticut"
[36] "Nebraska" "Idaho" "New Mexico" "Oregon" "Puerto Rico"
[41] "South Dakota" "North Dakota" "Rhode Island" "Montana" "West Virginia"
[46] "Delaware" "Alaska" "Wyoming" "New Hampshire" "District of Columbia"
[51] "Maine" "Hawaii" "Guam" "Vermont" "Virgin Islands"
[56] "Northern Mariana Islands"
data.us.latest
NA
NA
# List of top 20 state
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
k <- 20
data.us.top <- data.us.latest %>%
filter(ranking <= k+1) %>%
arrange(ranking)
us.state.top <- data.us.top %>% pull(state) %>% as.character()
us.state.top %>% setdiff('US') %>% print()
[1] "California" "Texas" "Florida" "Illinois" "New York" "Ohio" "Georgia" "Pennsylvania"
[9] "Tennessee" "Michigan" "Wisconsin" "North Carolina" "Indiana" "Arizona" "New Jersey" "Minnesota"
[17] "Missouri" "Massachusetts" "Alabama" "Virginia"
# confirmed rate & death rate of top 20 state
g.rate <- data.us.latest %>% filter(state %in% us.state.top & state != "US") %>%
select(state, confirmed.rate, death.rate, ranking) %>%
gather(key = Type, value = Percent, -c(state, ranking)) %>%
ggplot(aes(x=reorder(state, -desc(ranking)), y=Percent, fill = Percent)) +
geom_bar(stat='identity') +
scale_fill_gradient(low = "#ebbc62", high = "#b42006") +
geom_text(aes(label=Percent, y=Percent), size=3, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Confirmed Rate & Death Rate of Top 20 State in US')) +
#scale_fill_continuous(name='State', labels=aes(Percent)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=8),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~Type, ncol=1, scales='free_y')
g.rate

us.confirmed.num <- data.us.top$confirmed[data.us.top$state == "US"] %>% as.numeric()
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
5: Unknown or uninitialised column: `Country`.
6: Unknown or uninitialised column: `Country`.
us.confirmed.num
[1] 18706036
data.us.top %<>% mutate(confirmed.per.us = (confirmed * 100 / us.confirmed.num) %>% round(1))
data.us.top
g.us.top1 <- data.us.top %>%
filter(state != "US") %>%
select(state, confirmed, confirmed.per.us, ranking) %>%
gather(key = Type, value = count, -c(state, ranking, confirmed.per.us)) %>%
ggplot(aes(fill = count, y = count, x = reorder(state, -desc(ranking)))) +
geom_bar(position = "dodge", stat = "identity") +
labs(title = "20 state in US with most confirmed cases") +
scale_fill_gradient(low = "#ebbc62", high = "#b42006") +
xlab("") +
ylab("Confirmed Cases") +
geom_text(aes(label=paste0(confirmed.per.us, "%")), size=3, vjust=-0.5) +
theme(axis.text.x = element_text(angle = 90,vjust = 0.5))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
g.us.top2 <- data.us.top %>%
filter(state != "US") %>%
select(state, Male.confirmed, Female.confirmed, Male.deaths, Female.deaths, ranking) %>%
gather(key = Type, value = count, -c(state, ranking)) %>%
ggplot(aes(fill = Type, y = count, x = reorder(state, -desc(ranking)))) +
geom_bar(position = "stack", stat = "identity") +
labs(title = "20 most confirmed cases's state in US (Divided by gender)") +
xlab("") +
ylab("Cases") +
theme(axis.text.x = element_text(angle = 45,vjust = 1)) +
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
g.us.top3 <- data.us.top %>%
filter(state != "US") %>%
select(state, confirmed.per.us, ranking) %>%
gather(key = Type, value = count, -c(state, ranking)) %>%
ggplot(aes(x = "", y = confirmed.per.us, fill = confirmed.per.us)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y") +
theme_void() +
geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
guides(fill = guide_legend(reverse = TRUE))
g.us.top1

gly.us.top1 <- ggplotly(g.us.top1)
gly.us.top1
NA
NA
data.us.latlong <- df_us_latlong %>%
Warning messages:
1: Unknown or uninitialised column: `Country`.
2: Unknown or uninitialised column: `Country`.
3: Unknown or uninitialised column: `Country`.
4: Unknown or uninitialised column: `Country`.
rename("lat" = "latitude", "long" = "longitude")
#label_world <- lat.long
#label_world$avg_temp <- as.numeric(label_world[, names(label_world) %in% c("avg_temp")])
#label_world <- label_world %>%
# mutate(txt=paste0('<b>',ranking, '</b>',
# '<br/>','<b>',country, '</b>',
# '<br/>', "Temperature: ",avg_temp, ' °C',
# '<br/>', "Confirmed: ", confirmed,
# '<br/>', "Deaths: ", deaths,
# '<br/>', "Recovered: ", recovered
# ))
#label_world$txt <- label_world$txt %>% lapply(htmltools::HTML)
#label_world
map.us <- data.us.latest %>%
select(state, confirmed, confirmed.rate, deaths, death.rate, ranking) %>%
merge(data.us.latlong) %>%
mutate()
map.us
mergcountry = function(data1,data2){
data <- merge(x = data1, y = data2, by = "country", all.x = TRUE)
return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>%
select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>%
rename(GDP="GDP (millions of US dollars)")
data.top.world <- merge(x = data.top.world, y = df_healt, by = "country", all.x = TRUE) %>%
rename(healthcare="healthCareIndex")
data.top.world <- merge(x = data.top.world, y = df_pornhub, by = "country", all.x = TRUE) %>%
rename(Pornhub = "PornhubIndex(%)")
data.top.world <- mergcountry(data.top.world, df_temp)
index <- is.na(data.top.world)
data.top.world[index] <- 0
data.top.world
#View(data.top.world)
normalize = function(data){
#return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
z <- scale(data);
tanh(z/2)
}
norm_data = as.data.frame(apply(data.top.world[,2:12],2,normalize))
corr_data <- norm_data
norm_data$country <- c("Argentina","Bangladesh","Brazil","Chile","Colombia","France","Germany","India","Iran","Italy","Mexico","Pakistan","Peru","Russia","saudi Arabia","South Africa","Spain","Turkey","United Kingdom","US")
View(norm_data)
norm_data_plot <- select(norm_data,"country","confirm.rate","death.rate","recover.rate","healthcare","Pornhub","GDP","avg_temp")
norm_data_plot %<>% gather(key=type, value=count, -c(country))
level_order <- factor(norm_data_plot$type,
level = c("GDP","avg_temp","healthcare","recover.rate","death.rate","confirm.rate","Pornhub"))
ggplot(data = norm_data_plot, aes(x=country, y=level_order, fill=count)) +
geom_tile() +
scale_fill_gradient(low = "pink", high = "blue") +
xlab("") +
ylab("") +
theme_bw() +
theme(axis.text.x = element_text(angle = 90,vjust = 1))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)

#correlation
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate, healthcare, avg_temp, Pornhub))
head(corr_data)
cor(corr_data)
ggcorrplot(cor(corr_data),hc.order = TRUE,
outline.color = "white",
colors = c("#6D9EC1","white","#E46726"),
lab = TRUE)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdyaWQiKQ0KI2luc3RhbGwucGFja2FnZXMoIk1MUk1QQSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHByZXAiKQ0KI2luc3RhbGwucGFja2FnZXMoIm5vcm1hbHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdnY29ycnBsb3QiKQ0KI2luc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpDQojaW5zdGFsbC5wYWNrYWdlcygicmdkYWwiKQ0KI2luc3RhbGwucGFja2FnZXMoImpzb25saXRlIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQ0KI2luc3RhbGwucGFja2FnZXMoInJlYWRyIikNClN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsICJFbmdsaXNoIikNClN5cy5zZXRlbnYoIkxBTkdVQUdFIj0iRW4iKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KG5vcm1hbHIpDQpsaWJyYXJ5KGdnY29ycnBsb3QpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShyZWFkcikNCg0KIz8/c3JjX215c3FsDQpteV9kYiA8LSBzcmNfbXlzcWwoDQogIGRibmFtZSA9ICJjb3ZpZCIsDQogIGhvc3QgPSAibG9jYWxob3N0IiwNCiAgdXNlciA9ICJyb290IiwNCiAgcGFzc3dvcmQgPSAiMTIzNCINCikNCm15X2RiDQoNCiMjaW1wb3J0IGRhdGENCmRmX2NvbmYgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWQxOV9jb25maXJtZWQiKSkNCmRmX2NvbmYgPC0gYXMuZGF0YS5mcmFtZShkZl9jb25mKQ0KZGZfY29uZg0KZGZfZGVhdGhzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkMTlfZGVhdGhzIikpDQpkZl9kZWF0aHMgPC0gYXMuZGF0YS5mcmFtZShkZl9kZWF0aHMpDQpkZl9kZWF0aHMNCmRmX3JlY292ZXIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWQxOV9yZWNvdmVyZWQiKSkNCmRmX3JlY292ZXIgPC0gYXMuZGF0YS5mcmFtZShkZl9yZWNvdmVyKQ0KZGZfcmVjb3Zlcg0KYGBgDQoNCmBgYHtyfQ0KIyNjaGVjayB0aGUgdGltZSBmcmFtZSBvZiB0aGUgZGF0YQ0Kbi5jb2wgPC0gbmNvbChkZl9jb25mKQ0KZGF0ZXMgPC0gbmFtZXMoZGZfY29uZilbNTpuLmNvbF0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzKQ0KbWluLmRhdGUgPC0gbWluKGRhdGVzKQ0KbWF4LmRhdGUgPC0gbWF4KGRhdGVzKQ0KbWluLmRhdGUudHh0IDwtIG1pbi5kYXRlICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dCA8LSBtYXguZGF0ZSAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpgYGANCmBgYHtyfQ0KI2NsZWFuIGRhdGENCmNsZWFuRGF0YSA8LSBmdW5jdGlvbihkYXRhKSB7DQogICMjIHJlbW92ZSBzb21lIGNvbHVtbnMNCiAgZGF0YSAlPD4lIHNlbGVjdCgtYyhQcm92aW5jZS5TdGF0ZSwgTGF0LCBMb25nKSkgJT4lIHJlbmFtZShjb3VudHJ5PUNvdW50cnkuUmVnaW9uKQ0KICAjIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KICBkYXRhICU8PiUgZ2F0aGVyKGtleT1kYXRlLCB2YWx1ZT1jb3VudCwgLWNvdW50cnkpDQogICMjIGNvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gZGF0ZQ0KICBkYXRhICU8PiUgbXV0YXRlKGRhdGUgPSBkYXRlICU+JSBtZHkoKSkNCiAgIyMgYWdncmVnYXRlIGJ5IGNvdW50cnkNCiAgZGF0YSAlPD4lIGdyb3VwX2J5KGNvdW50cnksIGRhdGUpICU+JSBzdW1tYXJpc2UoY291bnQ9c3VtKGNvdW50LCBuYS5ybT1UKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KICByZXR1cm4oZGF0YSkNCn0NCiMjIGNsZWFuIHRoZSB0aHJlZSBkYXRhIHNldHMNCmRhdGEuY29uZmlybWVkIDwtIGRmX2NvbmYgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUoY29uZmlybWVkPWNvdW50KQ0KZGF0YS5kZWF0aHMgPC0gZGZfZGVhdGhzICU+JSBjbGVhbkRhdGEoKSAlPiUgcmVuYW1lKGRlYXRocz1jb3VudCkNCmRhdGEucmVjb3ZlcmVkIDwtIGRmX3JlY292ZXIgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUocmVjb3ZlcmVkPWNvdW50KQ0KZGF0YSA8LSBkYXRhLmNvbmZpcm1lZCAlPiUgbWVyZ2UoZGF0YS5kZWF0aHMsIGFsbD1UKSAlPiUgbWVyZ2UoZGF0YS5yZWNvdmVyZWQsIGFsbD1UKQ0KZGF0YQ0KIyMgY291bnRyaWVzL3JlZ2lvbnMgd2l0aCBjb25maXJtZWQgY2FzZXMsIGV4Y2wuIGNydWlzZSBzaGlwcw0KY291bnRyaWVzIDwtIGRhdGEgJT4lIHB1bGwoY291bnRyeSkgJT4lIHNldGRpZmYoJ0NydWlzZSBTaGlwJykNCmRhdGENCmRhdGEud29ybGQgPC0gZGF0YSAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudHJ5PSdXb3JsZCcsDQogICAgICAgICAgICBjb25maXJtZWQgPSBzdW0oY29uZmlybWVkLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIGRlYXRocyA9IHN1bShkZWF0aHMsIG5hLnJtPVQpLA0KICAgICAgICAgICAgcmVjb3ZlcmVkID0gc3VtKHJlY292ZXJlZCwgbmEucm09VCkpDQpkYXRhICU8PiUgcmJpbmQoZGF0YS53b3JsZCkNCmRhdGENCmRhdGEgJTw+JSBtdXRhdGUoY3VycmVudC5jb25maXJtZWQgPSBjb25maXJtZWQgLSBkZWF0aHMgLSByZWNvdmVyZWQpDQpWaWV3KGRhdGEpDQpgYGANCmBgYHtyfQ0KIyBXb3JsZCBjb25maXJtZWQgY2FzZXMgbWFwDQp4IDwtIHJhdy5jb25maXJtZWQNCngkY29uZmlybWVkIDwtIHhbLCBuY29sKHgpXQ0KeCAlPD4lIHNlbGVjdChjKENvdW50cnkuUmVnaW9uLCBQcm92aW5jZS5TdGF0ZSwgTGF0LCBMb25nLCBjb25maXJtZWQpKSAlPiUNCm11dGF0ZSh0eHQ9cGFzdGUwKENvdW50cnkuUmVnaW9uLCAnIC0gJywgUHJvdmluY2UuU3RhdGUsICc6ICcsIGNvbmZpcm1lZCkpDQptIDwtIGxlYWZsZXQod2lkdGg9MTIwMCwgaGVpZ2h0PTgwMCkgJT4lIGFkZFRpbGVzKCkNCiMgY2lyY2xlIG1hcmtlciAodW5pdHMgaW4gcGl4ZWxzKQ0KbSAlPD4lIGFkZENpcmNsZU1hcmtlcnMoeCRMb25nLCB4JExhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICMgcmFkaXVzPTIrbG9nMih4JGNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgICAgICAgICByYWRpdXM9MC4wMypzcXJ0KHgkY29uZmlybWVkKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9J3JlZCcsIGZpbGxPcGFjaXR5PTAuMywNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXgkdHh0KQ0KIyB3b3JsZA0KbQ0KYGBgDQpgYGB7cn0NCiMjIENoaW5hDQptICU+JSBzZXRWaWV3KDk1LCAzNSwgem9vbT00KQ0KIyMgQXVzdHJhbGlhIGFuZCBOZXcgWmVhbGFuZA0KbSAlPiUgc2V0VmlldygxMzUsIC0yNywgem9vbT00KQ0KIyMgVVMgYW5kIENhbmFkYQ0KbSAlPiUgc2V0VmlldygtMTA1LCA0MCwgem9vbT00KQ0KIyMgRXVyb3BlDQptICU+JSBzZXRWaWV3KDEwLCA1MCwgem9vbT00KQ0KDQpgYGANCg0KDQpgYGB7cn0NCiMgbmV3LmNvbmZpcm1lZCBuZXcuZGVhdGhzIG5ldy5yZWNvdmVyZWQNCmRhdGEgJTw+JSBhcnJhbmdlKGNvdW50cnksIGRhdGUpDQpuIDwtIG5yb3coZGF0YSkNCmRheTEgPC0gbWluKGRhdGEkZGF0ZSkNCmRhdGEgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBjb25maXJtZWQgLSBsYWcoY29uZmlybWVkLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCByZWNvdmVyZWQgLSBsYWcocmVjb3ZlcmVkLCBuPTEpKSkNCmRhdGEgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShuZXcuY29uZmlybWVkIDwgMCwgMCwgbmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UobmV3LmRlYXRocyA8IDAsIDAsIG5ldy5kZWF0aHMpLA0KICAgICAgICAgICAgICAgICBuZXcucmVjb3ZlcmVkID0gaWZlbHNlKG5ldy5yZWNvdmVyZWQgPCAwLCAwLCBuZXcucmVjb3ZlcmVkKSkNCiMjIGRlYXRoIHJhdGUgYmFzZWQgb24gdG90YWwgZGVhdGhzIGFuZCByZWNvdmVyZWQgY2FzZXMNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS51cHBlciA9ICgxMDAgKiBkZWF0aHMgLyAoZGVhdGhzICsgcmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLnVwcGVyID0gaWZlbHNlKGlzLm5hbihyYXRlLnVwcGVyKSwgMCwgcmF0ZS51cHBlcikpDQoNCiMjIGxvd2VyIGJvdW5kOiBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGNvbmZpcm1lZCBjYXNlcw0KZGF0YSAlPD4lIG11dGF0ZShyYXRlLmxvd2VyID0gKDEwMCAqIGRlYXRocyAvIGNvbmZpcm1lZCkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLmxvd2VyID0gaWZlbHNlKGlzLm5hbihyYXRlLmxvd2VyKSwgMCwgcmF0ZS5sb3dlcikpDQoNCiMjIGRlYXRoIHJhdGUgYmFzZWQgb24gdGhlIG51bWJlciBvZiBkZWF0aC9yZWNvdmVyZWQgb24gZXZlcnkgc2luZ2xlIGRheQ0KZGF0YSAlPD4lIG11dGF0ZShyYXRlLmRhaWx5ID0gKDEwMCAqIG5ldy5kZWF0aHMgLyAobmV3LmRlYXRocyArIG5ldy5yZWNvdmVyZWQpKSAlPiUgcm91bmQoMSksDQogICAgICAgICAgICAgICAgIHJhdGUuZGFpbHkgPSBpZmVsc2UoaXMubmFuKHJhdGUuZGFpbHkpLCAwLCByYXRlLmRhaWx5KSkNCg0KVmlldyhkYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCmRhdGEubG9uZyA8LSBkYXRhICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBkYXRlLCBjb25maXJtZWQsIGN1cnJlbnQuY29uZmlybWVkLCByZWNvdmVyZWQsIGRlYXRocykpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBkYXRlKSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YS5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCBjb25maXJtZWQ9J1RvdGFsIENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY292ZXJlZD0nUmVjb3ZlcmVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGhzPSdEZWF0aHMnKSkNClZpZXcoZGF0YS5sb25nKQ0KYGBgDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5sb25nICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMpICU8PiUNCiAgbXV0YXRlKGNvdW50cnk9Y291bnRyeSAlPiUgZmFjdG9yKGxldmVscz1jKHRvcC5jb3VudHJpZXMpKSkNCmRmICU+JSBmaWx0ZXIoY291bnRyeSAhPSAnV29ybGQnICYgdHlwZSAhPSAnVG90YWwgQ29uZmlybWVkJykgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50LCBjb2xvcj10eXBlKSkgKw0KICBnZW9tX2xpbmUoKSsNCiAgI2dlb21fYXJlYShhbHBoYT0wLjUpICsNCiMgeGxhYignJykgKyB5bGFiKCcnKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENPVklELTE5IENhc2VzIGluIFRvcCAyMCBDb3VudHJpZXMgLSAnLA0KICAgICAgICAgICAgICAgICAgICBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQua2V5LnNpemU9dW5pdCgwLjQsICdjbScpLA0KICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIHN0cmlwLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgZmFjZXRfd3JhcCh+Y291bnRyeSwgbmNvbD00LCBzY2FsZXM9J2ZyZWVfeScpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KIyNOdW1iZXIgb2YgY2FzZSBXb3JsZA0Kd29ybGQgPC0gZmlsdGVyKGRhdGEubG9uZyxjb3VudHJ5ID09ICdXb3JsZCcpDQp3b3JsZA0KcGxvdDEgPC0gd29ybGQgJT4lIGZpbHRlcih0eXBlICE9ICdUb3RhbCBDb25maXJtZWQnKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQpKSArDQogIGdlb21fYXJlYShhZXMoZmlsbD10eXBlKSwgYWxwaGE9MC41KSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENhc2VzIFdvcmxkd2lkZSAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoJ3JlZCcsICdncmVlbicsICdibGFjaycpKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQua2V5LnNpemU9dW5pdCgwLjIsICdjbScpLA0KICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT02KSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gd29ybGQgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPXR5cGUpKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENhc2VzIFdvcmxkd2lkZSAobG9nIHNjYWxlKSAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdwdXJwbGUnLCAncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuMiwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCg0KZ2x5LnBsb3QxIDwtIGdncGxvdGx5KHBsb3QxKQ0KZ2x5LnBsb3QxDQoNCmdseS5wbG90MiA8LSBnZ3Bsb3RseShwbG90MikNCmdseS5wbG90Mg0KYGBgDQoNCmBgYHtyfQ0KIyMgQ3VycmVudCBDb25maXJtZWQgQ2FzZXMNCmRhdGEud29ybGQgPC0gZGF0YSAlPiUgZmlsdGVyKGNvdW50cnk9PSdXb3JsZCcpDQpuIDwtIG5yb3coZGF0YS53b3JsZCkNCnBsb3QxIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PWN1cnJlbnQuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQ3VycmVudCBDb25maXJtZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nRGFpbHkgTmV3IENvbmZpcm1lZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQ0KDQpWaWV3KGRhdGEud29ybGQpDQpgYGANCmBgYHtyfQ0KIyMgYSBzY2F0dGVyIHBsb3Qgd2l0aCBhIHNtb290aGVkIGxpbmUgYW5kIHZlcnRpY2FsIHgtYXhpcyBsYWJlbHMNCnBsb3QxIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PWRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0FjY3VtdWxhdGl2ZSBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdBY2N1bXVsYXRpdmUgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDMgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90NCA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcucmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nTmV3IFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgZm91ciBwbG90cyB0b2dldGhlciwgd2l0aCAyIHBsb3RzIGluIGVhY2ggcm93DQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBwbG90MywgcGxvdDQsIG5yb3c9MikNCg0KYGBgDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQsIGZvciBkcmF3aW5nIGFyZWEgcGxvdHMNCnJhdGVzLmxvbmcgPC0gZGF0YSAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgZGF0ZSwgcmF0ZS51cHBlciwgcmF0ZS5sb3dlciwgcmF0ZS5kYWlseSkpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBkYXRlKSkNCiMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpyYXRlcy5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCByYXRlLmRhaWx5PSdEYWlseScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhdGUudXBwZXI9J1VwcGVyIGJvdW5kJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmF0ZS5sb3dlciA9ICdMb3dlciBib3VuZCcpKSANCiNWaWV3KHJhdGVzLmxvbmcpIA0KDQpnIDwtIHJhdGVzLmxvbmcgJT4lIGZpbHRlcihjb3VudHJ5ID09ICJXb3JsZCIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGNvdW50LCBjb2xvciA9IHR5cGUpKSArIA0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiV29ybGQgRGVhdGggUmF0ZSAoJSkiKSArDQogIHhsYWIoIiIpICsgeWxhYigiRGVhdGggUmF0ZSAoJSkiKQ0KDQpnDQoNCmdseS5kZWF0aCA8LSBnZ3Bsb3RseShnKQ0KZ2x5LmRlYXRoDQpgYGANCg0KYGBge3J9DQojIyByYW5raW5nIGJ5IGNvbmZpcm1lZCBjYXNlcw0KZGF0YS5sYXRlc3QuYWxsIDwtIGRhdGEgJT4lIGZpbHRlcihkYXRlID09IG1heChkYXRlKSkgJT4lDQogIHNlbGVjdChjb3VudHJ5LCBkYXRlLGNvbmZpcm1lZCwgbmV3LmNvbmZpcm1lZCwgY3VycmVudC5jb25maXJtZWQsDQogICAgICAgICByZWNvdmVyZWQsIGRlYXRocywgbmV3LmRlYXRocywgZGVhdGgucmF0ZT1yYXRlLmxvd2VyKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSBkZW5zZV9yYW5rKGRlc2MoY29uZmlybWVkKSkpICU+JQ0KICBhcnJhbmdlKHJhbmtpbmcpDQpWaWV3KGRhdGEubGF0ZXN0LmFsbCkNCg0KayA8LSAyMA0KIyMgdG9wIDIwIGNvdW50cmllczogMjEgaW5jbC4gJ1dvcmxkJw0KdG9wLmNvdW50cmllcyA8LSBkYXRhLmxhdGVzdC5hbGwgJT4lIGZpbHRlcihyYW5raW5nIDw9IGsgKyAxKSAlPiUNCiAgYXJyYW5nZShyYW5raW5nKSAlPiUgcHVsbChjb3VudHJ5KSAlPiUgYXMuY2hhcmFjdGVyKCkNCnRvcC5jb3VudHJpZXMgJT4lIHNldGRpZmYoJ1dvcmxkJykgJT4lIHByaW50KCkNCmBgYA0KDQpgYGB7cn0NCmRhdGEubGF0ZXN0IDwtIGRhdGEubGF0ZXN0LmFsbCAlPiUgZmlsdGVyKCFpcy5uYShjb3VudHJ5KSkgJT4lDQogIG11dGF0ZShjb3VudHJ5PWlmZWxzZShyYW5raW5nIDw9IGsgKyAxLCBhcy5jaGFyYWN0ZXIoY291bnRyeSksICdPdGhlcnMnKSkgJT4lDQogIG11dGF0ZShjb3VudHJ5PWNvdW50cnkgJT4lIGZhY3RvcihsZXZlbHM9Yyh0b3AuY291bnRyaWVzLCAnT3RoZXJzJykpKQ0KZGF0YS5sYXRlc3QgJTw+JSBncm91cF9ieShjb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXNlKGNvbmZpcm1lZD1zdW0oY29uZmlybWVkKSwgbmV3LmNvbmZpcm1lZD1zdW0obmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD1zdW0oY3VycmVudC5jb25maXJtZWQpLA0KICAgICAgICAgICAgcmVjb3ZlcmVkPXN1bShyZWNvdmVyZWQpLCBkZWF0aHM9c3VtKGRlYXRocyksIG5ldy5kZWF0aHM9c3VtKG5ldy5kZWF0aHMpKSAlPiUNCiAgbXV0YXRlKGRlYXRoLnJhdGU9KDEwMCAqIGRlYXRocy9jb25maXJtZWQpICU+JSByb3VuZCgxKSkgDQpkYXRhLmxhdGVzdA0KZGF0YS5sYXRlc3QgJTw+JSBzZWxlY3QoYyhjb3VudHJ5LCBjb25maXJtZWQsIGRlYXRocywgZGVhdGgucmF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywgY3VycmVudC5jb25maXJtZWQscmVjb3ZlcmVkKSkgJT4lDQogIG11dGF0ZShyZWNvdmVyLnJhdGU9KDEwMCAqIHJlY292ZXJlZC9jb25maXJtZWQpICU+JSByb3VuZCgxKSkNCmRhdGEubGF0ZXN0DQoNCmRmX3BvcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBwb3B1bGF0aW9uICIpKQ0KZGZfcG9wIDwtIGFzLmRhdGEuZnJhbWUoZGZfcG9wKQ0KZGZfcG9wIDwtIHJlbmFtZShkZl9wb3AsImNvdW50cnkiPSJDb3VudHJ5IikNCg0KIyBBZGQgV29ybGQgUG9wdWxhdGlvbg0Kd29ybGRfcG9wIDwtIHN1bShkZl9wb3AkYFBvcHVsYXRpb24gKDIwMjApYCkNCmRmX3BvcFtucm93KGRmX3BvcCkgKyAxLF0gPSBjKCJXb3JsZCIsIHdvcmxkX3BvcCkNCg0KIyBBZGQgT3RoZXIgQ291bnRyaWVzIFBvcHVsYXRpb24NCnRvcF9wb3AgPC0gZmlsdGVyKGRmX3BvcCwgZGZfcG9wJGNvdW50cnkgJWluJSB0b3AuY291bnRyaWVzICYgZGZfcG9wJGNvdW50cnkgIT0gIldvcmxkIikNCnRvcF9wb3AgPC0gc3VtKHRvcF9wb3AkYFBvcHVsYXRpb24gKDIwMjApYCAlPiUgYXMubnVtZXJpYygpKQ0Kb3RoZXJzX3BvcCA8LSAod29ybGRfcG9wIC0gdG9wX3BvcCkgDQpkZl9wb3BbbnJvdyhkZl9wb3ApICsgMSxdID0gYygiT3RoZXJzIiwgb3RoZXJzX3BvcCkNClZpZXcoZGZfcG9wKQ0KDQpkYXRhLmxhdGVzdCA8LSBtZXJnZSh4ID0gZGF0YS5sYXRlc3QsIHkgPSBkZl9wb3AsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpIA0KZGF0YS5sYXRlc3QNCmRhdGEubGF0ZXN0IDwtIHJlbmFtZShkYXRhLmxhdGVzdCwicG9wdWxhdGlvbiIgPSAiUG9wdWxhdGlvbiAoMjAyMCkiKQ0KZGF0YS5sYXRlc3QkcG9wdWxhdGlvbiA8LSBkYXRhLmxhdGVzdCRwb3B1bGF0aW9uICU+JSBhcy5udW1lcmljKCkNCmRhdGEubGF0ZXN0ICA8LSBkYXRhLmxhdGVzdCAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgY29uZmlybWVkLCBkZWF0aHMsIGRlYXRoLnJhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5jb25maXJtZWQsIG5ldy5kZWF0aHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkLCByZWNvdmVyZWQsIHJlY292ZXIucmF0ZSwgcG9wdWxhdGlvbikpICU+JQ0KICBtdXRhdGUoY29uZmlybS5yYXRlID0gKDEwMCAqIGNvbmZpcm1lZCAvIHBvcHVsYXRpb24pICU+JSByb3VuZCgxKSkNCmRhdGEubGF0ZXN0DQpgYGANCmBgYHtyfQ0KZGF0YS5sYXRlc3QgJT4lIG11dGF0ZShkZWF0aC5yYXRlPWRlYXRoLnJhdGUgJT4lIGZvcm1hdChuc21hbGw9MSkgJT4lIHBhc3RlMCgnJScpKQ0KDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KZGF0YS5sYXRlc3QubG9uZyA8LSBkYXRhLmxhdGVzdCAlPiUgZmlsdGVyKGNvdW50cnkhPSdXb3JsZCcpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtY291bnRyeSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSB3aXRoIHByb3BlciB0ZXh0IGFuZCBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YS5sYXRlc3QubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm1lZD0nVG90YWwgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRocz0nVG90YWwgRGVhdGhzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRoLnJhdGU9J0RlYXRoIFJhdGUgKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5jb25maXJtZWQ9J05ldyBDb25maXJtZWQgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5kZWF0aHM9J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdmVyLnJhdGUgPSAnUmVjb3ZlcmVkIFJhdGUoJSknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZmlybS5yYXRlID0gJ0NvbmZpcm1lZCBSYXRlKCUpJykpDQojVmlldyhkYXRhLmxhdGVzdC5sb25nKQ0KZGF0YS5vbmUuZGVtIDwtIGZpbHRlcihkYXRhLmxhdGVzdC5sb25nLHR5cGU9PSdUb3RhbCBDb25maXJtZWQnDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J1RvdGFsIERlYXRocycNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nQ3VycmVudCBDb25maXJtZWQnKQ0KZGF0YS50d28uZGVtIDwtIGZpbHRlcihkYXRhLmxhdGVzdC5sb25nLHR5cGU9PSdEZWF0aCBSYXRlICglKScNCiAgICAgICAgICAgICAgICAgICAgICMgIHwgdHlwZT09J05ldyBDb25maXJtZWQgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJw0KICAgICAgICAgICAgICAgICAgICAjICAgfCB0eXBlPT0nTmV3IERlYXRocyAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J1JlY292ZXJlZCBSYXRlKCUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdDb25maXJtZWQgUmF0ZSglKScpDQpkYXRhLnR3by5kZW0NCmBgYA0KDQpgYGB7cn0NCiMjIGJhciBjaGFydA0KZGF0YS5vbmUuZGVtICU+JSBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9Mywgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMyksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH50eXBlLCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykNCg0KYGBgDQpgYGB7cn0NCmRhdGEudHdvLmRlbSRmYWNldCA8LSBmYWN0b3IoZGF0YS50d28uZGVtJHR5cGUsIGxldmVscyA9IGMoJ0NvbmZpcm1lZCBSYXRlKCUpJywgJ1JlY292ZXJlZCBSYXRlKCUpJywnRGVhdGggUmF0ZSAoJSknKSkNCmRhdGEudHdvLmRlbSAlPiUgDQogIGdncGxvdChhZXMoeD1jb3VudHJ5LCB5PWNvdW50LCBmaWxsPWNvdW50cnksIGdyb3VwPWNvdW50cnkpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50LCB5PWNvdW50KSwgc2l6ZT00LCB2anVzdD0wKSArDQogIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnVG9wIDIwIENvdW50cmllcyB3aXRoIE1vc3QgQ29uZmlybWVkIENhc2VzIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9J0NvdW50cnknLCBsYWJlbHM9YWVzKGNvdW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTAsYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAofmZhY2V0LCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykNCmBgYA0KDQpgYGB7cn0NCiMjR0RQDQpkZl9nZHAgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZ2RwIikpDQpkZl9nZHAgPC0gYXMuZGF0YS5mcmFtZShkZl9nZHApDQpkZl9nZHAgPC0gcmVuYW1lKGRmX2dkcCwiY291bnRyeSI9IlJlYWwgR0RQIGdyb3d0aCAoQW5udWFsIHBlcmNlbnQgY2hhbmdlKSIpDQpkZl9nZHAgPC0gc2VsZWN0KGRmX2dkcCxjKCJjb3VudHJ5IiwiMjAxMiIsIjIwMTMiLCIyMDE0IiwiMjAxNSIsIjIwMTYiLCIyMDE3IiwiMjAxOCIsIjIwMTkiLCIyMDIwIiwiMjAyMSIpKQ0KZGZfZ2RwDQpkZl9nZHAyMDE5IDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGdkcDE5IikpDQpkZl9nZHAyMDE5IDwtIGFzLmRhdGEuZnJhbWUoZGZfZ2RwMjAxOSkNCmRmX2dkcDIwMTkNCg0KYGBgDQpgYGB7cn0NCiNoZWFsdGhyYW5raW5nDQpkZl9oZWFsdCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBoZWFsdGhyYW5raW5nIikpDQpkZl9oZWFsdCA8LSBhcy5kYXRhLmZyYW1lKGRmX2hlYWx0KQ0KZGZfaGVhbHQgPC0gc2VsZWN0KGRmX2hlYWx0LGMoImNvdW50cnkiLCJoZWFsdGhDYXJlSW5kZXgiKSkNClZpZXcoZGZfaGVhbHQpDQpgYGANCg0KYGBge3J9DQojVG9wMjBQb3JuaHViDQpkZl9wb3JuaHViIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIFBvcm5odWIiKSkNCmRmX3Bvcm5odWIgPC0gYXMuZGF0YS5mcmFtZShkZl9wb3JuaHViKQ0KZGZfcG9ybmh1Yg0KYGBgDQoNCmBgYHtyfQ0KI3RlbXANCmRmX3RlbXAgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gd29ybGRfdGVtcCIpKQ0KZGZfdGVtcCA8LSBhcy5kYXRhLmZyYW1lKGRmX3RlbXApIA0KZGZfdGVtcCRDb3VudHJ5W2RmX3RlbXAkQ291bnRyeSA9PSAiVW5pdGVkIFN0YXRlcyJdIDwtICJVUyINCg0KZGZfY2l0eSA8LSBzZWxlY3QoZGZfdGVtcCxjKCJDb3VudHJ5IiwiQ2l0eSIpKSAlPiUNCiAgcmVuYW1lKGNvdW50cnk9Q291bnRyeSkgJT4lIA0KICByZW5hbWUoY2l0eT1DaXR5KQ0KDQpudW1vZmNpdHkgPC0gYWdncmVnYXRlKGNpdHkgfiBjb3VudHJ5LCBkYXRhID0gZGZfY2l0eSwgbGVuZ3RoKQ0KDQpkZl90ZW1wIDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJBdmdfWWVhciIpKSAlPiUNCiAgcmVuYW1lKGNvdW50cnk9Q291bnRyeSkNClZpZXcoZGZfdGVtcCkNCg0KI2RmX3RlbXAgPC0gZGF0YS5mcmFtZShjb3VudHJ5PWRmX3RlbXBbLDFdLGF2Zz1yb3dNZWFucyhkZl90ZW1wWywtMV0pKQ0KZGZfdGVtcCA8LSBkZl90ZW1wICU8PiUgZ3JvdXBfYnkoY291bnRyeSkgJT4lIHN1bW1hcmlzZShhdmdfdGVtcCA9IG1lYW4oQXZnX1llYXIsbmEucm0gPSBUUlVFKSU+JSByb3VuZCgxKSkNCmRmX3RlbXANCmBgYA0KDQpgYGB7cn0NCiNkaXNwbGF5LmJyZXdlci5hbGwoKQ0KZGZfdGVtcC5hbGwgPC0gZGZfdGVtcCAlPiUgbWVyZ2UoZGF0YS5sYXRlc3QuYWxsKQ0KVmlldyhkZl90ZW1wLmFsbCkNCmRmX3RlbXBfdG9wLmFsbCA8LSBkZl90ZW1wLmFsbCAlPiUgZmlsdGVyKGNvdW50cnkgJWluJSB0b3AuY291bnRyaWVzKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSByYW5raW5nIC0gMSkgJT4lDQogIGFycmFuZ2UocmFua2luZykNClZpZXcoZGZfdGVtcF90b3ApDQpnX3RlbXBfdG9wIDwtIGRmX3RlbXBfdG9wICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKGNvdW50cnksIHJhbmtpbmcpLCB5ID0gYXZnX3RlbXAsIGZpbGwgPSBhdmdfdGVtcCkpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoIlRlbXBlcmF0dXJlIGluIFRvcCAgMjAgY291bnRyaWVzIiksIHN1YnRpdGxlID0gIkF2ZXJhZ2UgVGVtcGVyYXR1cmUgaW4gVG9wIDIwIGNvdW50cmllcyB3aXRoIG1vc3QgY29uZmlybWVkIGNhc2VzICjCsEMpICgyMDIwKSIpICsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gIiM5M0RCRkYiLCBoaWdoID0gIiNGRjc3NzEiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9YXZnX3RlbXAsIHk9YXZnX3RlbXApLCBzaXplPTQsIHZqdXN0PS0wLjUpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICB0aGVtZSgNCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZSA9IDE1LCBoanVzdCA9IDAuNSksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBoanVzdCA9IDAuNSksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemUgPSA5LCBhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIkNvdW50cnkiKSArDQogIHNjYWxlX3lfZGlzY3JldGUobmFtZSA9ICJBdmVyYWdlIFRlbXBlcmF0dXJlIikNCg0KICAgICAgICAgICAjbGFicyh0aXRsZSA9ICJUZW1wZXJhdHVyZSBpbiBUb3AgIDIwIGNvdW50cmllcyIsIHN1YnRpdGxlID0gIlRlbXBlcmF0dXJlIGluIFRvcCAyMCBjb3VudHJpZXMgd2l0aCBtb3N0IGNvbmZpcm1lZCBjYXNlcyAowrBDKSIpDQogICAgICAgICANCiNnX3RlbXBfdG9wICAgICAgICAgDQpnX3RlbXBfdG9wIA0KYGBgDQoNCg0KYGBge3J9DQojZGZfY29uZg0KI2RhdGEubGF0ZXN0LmFsbA0KbGF0LmxvbmcgPC0gcmVuYW1lKGRmX2NvbmYsICJjb3VudHJ5IiA9ICJDb3VudHJ5LlJlZ2lvbiIsICJjaXR5IiA9ICJQcm92aW5jZS5TdGF0ZSIpICU+JSANCiAgc2VsZWN0KCJjb3VudHJ5IiwgIkxhdCIsICJMb25nIikgJT4lIA0KICBtZXJnZShkZl90ZW1wLmFsbFtjKCJjb3VudHJ5IiwiY29uZmlybWVkIiwgInJlY292ZXJlZCIsICJkZWF0aHMiLCAiYXZnX3RlbXAiLCAicmFua2luZyIpXSwgYnkgPSAiY291bnRyeSIpICU+JQ0KICBkaXN0aW5jdChjb3VudHJ5LCAua2VlcF9hbGwgPSBUUlVFKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSByYW5raW5nIC0gMSkgJT4lDQogIGFycmFuZ2UocmFua2luZykNClZpZXcobGF0LmxvbmcpDQpgYGANCg0KYGBge3J9DQpsYWJlbF93b3JsZCA8LSBsYXQubG9uZyANCmxhYmVsX3dvcmxkJGF2Z190ZW1wIDwtIGFzLm51bWVyaWMobGFiZWxfd29ybGRbLCBuYW1lcyhsYWJlbF93b3JsZCkgJWluJSBjKCJhdmdfdGVtcCIpXSkNCmxhYmVsX3dvcmxkIDwtIGxhYmVsX3dvcmxkICU+JSAgDQogIG11dGF0ZSh0eHQ9cGFzdGUwKCc8Yj4nLHJhbmtpbmcsICc8L2I+JywNCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywnPGI+Jyxjb3VudHJ5LCAnPC9iPicsDQogICAgICAgICAgICAgICAgICAgICc8YnIvPicsICJUZW1wZXJhdHVyZTogICIsYXZnX3RlbXAsICcgwrBDJywNCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIkNvbmZpcm1lZDogICIsIGNvbmZpcm1lZCwgDQogICAgICAgICAgICAgICAgICAgICc8YnIvPicsICJEZWF0aHM6ICIsIGRlYXRocywNCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIlJlY292ZXJlZDogIiwgcmVjb3ZlcmVkDQogICAgICAgICAgICAgICAgICAgICkpIA0KDQpsYWJlbF93b3JsZCR0eHQgPC0gbGFiZWxfd29ybGQkdHh0ICU+JSBsYXBwbHkoaHRtbHRvb2xzOjpIVE1MKQ0KbGFiZWxfd29ybGQgDQoNCmxhYmVsX3RvcCA8LSBsYWJlbF93b3JsZCAlPiUgZmlsdGVyKHJhbmtpbmcgPCAyMSkNCmxhYmVsX3RvcA0KYGBgDQoNCmBgYHtyfQ0Kd3BhbCA8LSBjb2xvck51bWVyaWMoIllsT3JSZCIsIGxhYmVsX3dvcmxkJGF2Z190ZW1wLCBuID0gNCkNCg0KdG9wSWNvbiA8LSBtYWtlSWNvbigic3Rhci5wbmciLA0KICAjaWNvblVybCA9ICJodHRwczovL3N0YXRpYy52ZWN0ZWV6eS5jb20vc3lzdGVtL3Jlc291cmNlcy9wcmV2aWV3cy8wMDEvMTg5LzA2My9ub25fMngvc3Rhci1yb3VuZGVkLXBuZy5wbmciLA0KICBpY29uV2lkdGggPSAxMCwgaWNvbkhlaWdodCA9IDEwDQogICNpY29uQW5jaG9yWCA9IDIwLCBpY29uQW5jaG9yWSA9IDIwDQogIA0KKQ0KDQpsYWJlbF93b3JsZCA8LSBsYWJlbF93b3JsZCAlPiUgZmlsdGVyKHJhbmtpbmcgPiAyMCkgDQogIA0KbSA8LSBsZWFmbGV0KHdpZHRoPTEyMDAsIGhlaWdodD04MDApICU+JSBhZGRUaWxlcygpICANCm0gJTw+JSAgYWRkQ2lyY2xlTWFya2VycyhsYWJlbF93b3JsZCRMb25nLCBsYWJlbF93b3JsZCRMYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAjIHJhZGl1cz0yK2xvZzIoeCRjb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmFkaXVzPTEwLCMqbG9nMihtLndvcmxkJGF2Zy50ZW1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgI2NvbG9yPSdyZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB3cGFsKGxhYmVsX3dvcmxkJGF2Z190ZW1wKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eT0wLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAjcG9wdXA9bGFiZWwudG9wJHR4dA0KICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9IGxhYmVsX3dvcmxkJHR4dCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gIldvcmxkIg0KICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUNCiAgDQogIGFkZENpcmNsZU1hcmtlcnMobGFiZWxfdG9wJExvbmcsIGxhYmVsX3RvcCRMYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAjIHJhZGl1cz0yK2xvZzIoeCRjb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmFkaXVzPTEwLCMqbG9nMihtLndvcmxkJGF2Zy50ZW1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgI2NvbG9yPSdyZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB3cGFsKGxhYmVsX3RvcCRhdmdfdGVtcCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHk9MC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgI3BvcHVwPWxhYmVsLnRvcCR0eHQNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPSBsYWJlbF90b3AkdHh0LA0KICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiVG9wIDIwIENvdW50cmllcyINCiAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lDQogIA0KICBhZGRMYWJlbE9ubHlNYXJrZXJzKGxhYmVsX3RvcCRMb25nLCBsYWJlbF90b3AkTGF0LCBsYWJlbCA9IGxhYmVsX3RvcCRyYW5raW5nLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucyhub0hpZGUgPSBUUlVFLCB0ZXh0T25seSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiaGVhZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSBjKDUsNCkpLA0KICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gIlRvcCAyMCBDb3VudHJpZXMiKSAlPiUNCiAgDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLCBwYWwgPSB3cGFsLCB2YWx1ZXMgPSBsYWJlbF93b3JsZCRhdmdfdGVtcCwgb3BhY2l0eSA9IDEsDQogICAgICAgICAgICBsYWJGb3JtYXQgPSBsYWJlbEZvcm1hdChzdWZmaXggPSAiIMKwQyIpLA0KICAgICAgICAgICAgdGl0bGUgPSAiVGVtcGVyYXR1cmUiKSAlPiUgDQogIA0KICBhZGRMYXllcnNDb250cm9sKA0KICAgICNiYXNlR3JvdXBzID0gYygiT1NNIChkZWZhdWx0KSIsICJUb25lciIsICJUb25lciBMaXRlIiksDQogICAgb3ZlcmxheUdyb3VwcyA9IGMoIlRvcCAyMCBDb3VudHJpZXMiLCAiV29ybGQiKSwNCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpDQogICkNCm0NCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KI1RvcCAyMCB3aXRoIGdkcA0KZGF0YS5sb25nR0RQIDwtIGRmX2dkcCAlPiUgZ2F0aGVyKGtleT15ZWFyLCB2YWx1ZT1HRFAsIC1jKGNvdW50cnkpKQ0KZGF0YS50b3AgPC0gZGF0YS5sYXRlc3QgJT4lIGZpbHRlcihjb3VudHJ5IT0nV29ybGQnJiBjb3VudHJ5IT0nT3RoZXJzJykNCiNkYXRhLnRvcCA8LSBoZWFkKGRhdGEudG9wLDIwKQ0KVmlldyhkYXRhLnRvcCkNCmRhdGEuZ2RwIDwtIGZpbHRlcihkYXRhLmxvbmdHRFAseWVhcj09JzIwMjAnKQ0KVmlldyhkYXRhLmdkcCkNCmBgYA0KDQpgYGB7cn0NCiNyYW5rIEdEUA0KZGF0YS50b3AuaGlnaHQgPC0gZGF0YS5nZHAgJT4lIHNlbGVjdChjb3VudHJ5LCB5ZWFyLEdEUCkgJT4lDQogIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhkZXNjKEdEUCkpKQ0KI2RhdGEudG9wLmhpZ2h0DQoNCmsgPC0gMjANCg0KdG9wLmdkcCA8LSBkYXRhLnRvcC5oaWdodCAlPiUgDQogIGZpbHRlcihyYW5raW5nIDwgaykgJT4lIA0KICBhcnJhbmdlKHJhbmtpbmcpDQoNCg0KZGF0YS50b3AubG93IDwtIGRhdGEuZ2RwICU+JSBzZWxlY3QoY291bnRyeSwgeWVhcixHRFApICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoR0RQKSkNCmxvdy5nZHAubG9uZyA8LSBkYXRhLnRvcC5sb3cgJT4lIA0KICBmaWx0ZXIocmFua2luZyA8IGspICU+JSANCiAgYXJyYW5nZShyYW5raW5nKQ0KVmlldyhsb3cuZ2RwLmxvbmcpDQoNCg0KdG9wLmdkcA0KbG93LmdkcA0KYGBgDQoNCmBgYHtyfQ0KDQp3b3JsZC5nZHAgPC0gZGZfZ2RwICU+JSBmaWx0ZXIoY291bnRyeSA9PSAiV29ybGQiKSAlPiUNCiAgZ2F0aGVyKGtleSA9IFllYXIsIHZhbHVlID0gR0RQLCAtY291bnRyeSkNCndvcmxkLmdkcA0KDQp3b3JsZC5nZHAkZ3JvdXAgPC0gY3V0KHdvcmxkLmdkcCRHRFAsIGMoLUluZiwwLCBJbmYpLCBsYWJlbHMgPSBjKCItIiwiKyIpKQ0KDQpnIDwtIGdncGxvdCh3b3JsZC5nZHAsIGFlcyh4ID0gWWVhciwgeSA9IEdEUCwgZmlsbCA9IGdyb3VwKSkgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9R0RQLCB5ID0gR0RQKSwgc2l6ZT0zLCB2anVzdD0tMSkgKw0KICAgIGxhYnModGl0bGU9cGFzdGUwKCJSZWFsIEdEUCBncm93dGggKEFubnVhbCBwZXJjZW50IGNoYW5nZSkgb2YgV29ybGQgZnJvbSAyMDEyLTIwMjEiKSkrDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIi0iID0gInJlZCIsIisiID0gImxpbWVncmVlbiIpKSsNCiAgICB0aGVtZV9idygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsNCiAgICB4bGFiKCIiKSsNCiAgICB0aGVtZSgjbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKQ0KDQpnDQoNCmdseS53b3JsZC5nZHAgPC0gZ2dwbG90bHkoZykNCmdseS53b3JsZC5nZHANCmBgYA0KYGBge3J9DQpnZHAudG9wMjAgPC0gZGZfZ2RwMjAxOSAlPiUNCiAgc2VsZWN0KGMoInJhbmsiLCAiY291bnRyeSIsICJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikpICU+JQ0KICBtZXJnZShkYXRhLmxhdGVzdC5hbGwgJT4lIA0KICAgICAgICAgIHNlbGVjdChjb3VudHJ5LCByYW5raW5nLCBjb25maXJtZWQsIHJlY292ZXJlZCwgZGVhdGhzKSAlPiUgDQogICAgICAgICAgZmlsdGVyKGNvdW50cnkgJWluJSB0b3AuY291bnRyaWVzICYgY291bnRyeSAhPSAiV29ybGQiKSwgYnkgPSAiY291bnRyeSIpICU+JQ0KICBhcnJhbmdlKHJhbmtpbmcpICU+JSANCiAgbXV0YXRlKHJhbmtpbmcgPSByYW5raW5nIC0gMSkgDQpnZHAudG9wMjAgJTw+JSByZW5hbWUoIkdEUCIgPSAiR0RQIChtaWxsaW9ucyBvZiBVUyBkb2xsYXJzKSIpDQpnZHAudG9wMjANCg0KZyA8LSBnZ3Bsb3QoZ2RwLnRvcDIwLCBhZXMoeCA9IEdEUCwgeSA9IHJlb3JkZXIoY291bnRyeSwgLXJhbmtpbmcpKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gImlkZW50aXR5IiwgYWVzKGZpbGwgPSBHRFApKSsgDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoIkdEUCIsIGxvdyA9ICIjRkY0MDM4IiwgaGlnaCA9ICIjNTBFOTUyIikgKyANCiAgbGFicyh0aXRsZT1wYXN0ZTAoIkdEUCAgb2YgVG9wIDIwIENvdW50cmllcyBpbiAyMDE5IChtaWxsaW9ucyBvZiBVUyBkb2xsYXJzKSIpKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9R0RQLCB4ID0gR0RQKSwgc2l6ZT0zLjUsIGhqdXN0PS0wLjIpICsNCiAgeGxhYigiR0RQIChtaWxsaW9ucyBvZiBVUyBkb2xsYXJzKSIpICsNCiAgeWxhYigiIikgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKQ0KZw0KDQpnbHkudG9wLmdkcCA8LSBnZ3Bsb3RseShnKQ0KZ2x5LnRvcC5nZHANCmBgYA0KDQoNCmBgYHtyfQ0KZGZfc2FycyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBzYXJzXzIwMDMiKSkNCmRmX3NhcnMgPC0gYXMuZGF0YS5mcmFtZShkZl9zYXJzKQ0KZGZfc2Fycw0KDQpkYXRlcy5zIDwtIGRmX3NhcnNbLDFdJT4lIG1keSgpDQpyYW5nZShkYXRlcy5zKQ0KbWluLmRhdGUucyA8LSBtaW4oZGF0ZXMucykNCm1heC5kYXRlLnMgPC0gbWF4KGRhdGVzLnMpDQptaW4uZGF0ZS50eHQucyA8LSBtaW4uZGF0ZS5zICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dC5zIDwtIG1heC5kYXRlLnMgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KDQpgYGANCg0KYGBge3J9DQojIGNsZWFuIGRhdGEgU2FycyANCmRhdGEuc2FycyA8LSBkZl9zYXJzICU+JSByZW5hbWUoYygiZGF0ZSIgPSAiRGF0ZSIsICJjb25maXJtZWQiID0gIkN1bXVsYXRpdmVfbnVtYmVyIiAgLCJkZWF0aHMiID0gIk51bWJlcl9kZWF0aHMiLCAicmVjb3ZlcmVkIiA9ICJOdW1iZXJfcmVjb3ZlcmVkIikpICU+JQ0KICBtdXRhdGUoZGF0ZSA9IGRhdGUgJT4lIG1keSgpKSAlPiUNCiAgZ3JvdXBfYnkoY291bnRyeSwgZGF0ZSkgJT4lIGFzLmRhdGEuZnJhbWUoKSANClZpZXcoZGF0YS5zYXJzKQ0KDQoNCiMgQWRkIFdvcmxkJ3MgU2FycyBjYXNlcyANCndvcmxkLnNhcnMgPC0gZGF0YS5zYXJzICU+JSBncm91cF9ieShkYXRlKSAlPiUgDQogIHN1bW1hcmlzZShjb3VudHJ5PSdXb3JsZCcsDQogICAgICAgICAgICBjb25maXJtZWQgPSBzdW0oY29uZmlybWVkLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIGRlYXRocyA9IHN1bShkZWF0aHMsIG5hLnJtPVQpLA0KICAgICAgICAgICAgcmVjb3ZlcmVkID0gc3VtKHJlY292ZXJlZCwgbmEucm09VCkpDQoNCmRhdGEuc2FycyAlPD4lIHJiaW5kKHdvcmxkLnNhcnMpDQpkYXRhLnNhcnMgJTw+JSBtdXRhdGUoY3VycmVudC5jb25maXJtZWQgPSBjb25maXJtZWQgLSBkZWF0aHMgLSByZWNvdmVyZWQpDQojVmlldyh3b3JsZC5zYXJzKSANCiNWaWV3KGRhdGEuc2FycykgDQpgYGANCg0KYGBge3J9DQojcmF0ZQ0KZGF0YS5zYXJzICU8PiUgYXJyYW5nZShjb3VudHJ5LCBkYXRlKQ0KbiA8LSBucm93KGRhdGEuc2FycykNCmRheTEuc2FycyA8LSBtaW4oZGF0YS5zYXJzJGRhdGUpDQpkYXRhLnNhcnMgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShkYXRlID09IGRheTEuc2FycywgTkEsIGNvbmZpcm1lZCAtIGxhZyhjb25maXJtZWQsIG49MSkpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKGRhdGUgPT0gZGF5MS5zYXJzLCBOQSwgZGVhdGhzIC0gbGFnKGRlYXRocywgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5yZWNvdmVyZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLnNhcnMsIE5BLCByZWNvdmVyZWQgLSBsYWcocmVjb3ZlcmVkLCBuPTEpKSkNCmRhdGEuc2FycyAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKG5ldy5jb25maXJtZWQgPCAwLCAwLCBuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShuZXcuZGVhdGhzIDwgMCwgMCwgbmV3LmRlYXRocyksDQogICAgICAgICAgICAgICAgIG5ldy5yZWNvdmVyZWQgPSBpZmVsc2UobmV3LnJlY292ZXJlZCA8IDAsIDAsIG5ldy5yZWNvdmVyZWQpKQ0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0b3RhbCBkZWF0aHMgYW5kIHJlY292ZXJlZCBjYXNlcw0KZGF0YS5zYXJzICU8PiUgbXV0YXRlKHJhdGUudXBwZXIgPSAoMTAwICogZGVhdGhzIC8gKGRlYXRocyArIHJlY292ZXJlZCkpICU+JSByb3VuZCgxKSwNCiAgICAgICAgICAgICAgICAgcmF0ZS51cHBlciA9IGlmZWxzZShpcy5uYW4ocmF0ZS51cHBlciksIDAsIHJhdGUudXBwZXIpKQ0KDQojIyBsb3dlciBib3VuZDogZGVhdGggcmF0ZSBiYXNlZCBvbiB0b3RhbCBjb25maXJtZWQgY2FzZXMNCmRhdGEuc2FycyAlPD4lIG11dGF0ZShyYXRlLmxvd2VyID0gKDEwMCAqIGRlYXRocyAvIGNvbmZpcm1lZCkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLmxvd2VyID0gaWZlbHNlKGlzLm5hbihyYXRlLmxvd2VyKSwgMCwgcmF0ZS5sb3dlcikpDQoNCiMjIGRlYXRoIHJhdGUgYmFzZWQgb24gdGhlIG51bWJlciBvZiBkZWF0aC9yZWNvdmVyZWQgb24gZXZlcnkgc2luZ2xlIGRheQ0KZGF0YS5zYXJzICU8PiUgbXV0YXRlKHJhdGUuZGFpbHkgPSAoMTAwICogbmV3LmRlYXRocyAvIChuZXcuZGVhdGhzICsgbmV3LnJlY292ZXJlZCkpICU+JSByb3VuZCgxKSwNCiAgICAgICAgICAgICAgICAgcmF0ZS5kYWlseSA9IGlmZWxzZShpcy5uYW4ocmF0ZS5kYWlseSksIDAsIHJhdGUuZGFpbHkpKQ0KDQpWaWV3KGRhdGEuc2FycykNCmBgYA0KDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQpkYXRhLnNhcnMubG9uZyA8LSBkYXRhLnNhcnMgJT4lDQogIHNlbGVjdChjKGNvdW50cnksIGRhdGUsIGNvbmZpcm1lZCwgY3VycmVudC5jb25maXJtZWQsIHJlY292ZXJlZCwgZGVhdGhzKSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhLnNhcnMubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwgY29uZmlybWVkPSdUb3RhbCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD0nQ3VycmVudCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdmVyZWQ9J1JlY292ZXJlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRocz0nRGVhdGhzJykpDQpWaWV3KGRhdGEuc2Fycy5sb25nKQ0KDQojIFdvcmxkIHNhcnMnIGxvbmcgZGF0YSANCndvcmxkLnNhcnMubG9uZyA8LSBkYXRhLnNhcnMubG9uZyAlPiUNCiAgZmlsdGVyKGNvdW50cnkgPT0gIldvcmxkIikNClZpZXcod29ybGQuc2Fycy5sb25nKQ0KDQpnIDwtIGdncGxvdCh3b3JsZC5zYXJzLmxvbmcsIGFlcyhkYXRlLCBjb3VudCwgY29sb3IgPSB0eXBlKSkgKw0KICBnZW9tX2xpbmUoKSsNCiAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgQ2FzZXMgV29ybGR3aWRlOiBTQVJzIikrDQogIHhsYWIoIiIpKw0KICB5bGFiKCIiKQ0KZw0KDQpnbHkuZyA8LSBnZ3Bsb3RseShnKQ0KZ2x5LmcNCmdseS5wbG90Mg0KYGBgDQpgYGB7cn0NCg0KYGBgDQoNCg0KYGBge3J9DQojIyBDdXJyZW50IENvbmZpcm1lZCBDYXNlcw0KZGF0YS5zYXJzLndvcmxkIDwtIGRhdGEuc2FycyAlPiUgZmlsdGVyKGNvdW50cnk9PSdXb3JsZCcpDQpWaWV3KGRhdGEuc2Fycy53b3JsZCkNCm4gPC0gbnJvdyhkYXRhLnNhcnMud29ybGQpDQpWaWV3KGRhdGEuc2Fycy53b3JsZCkNCnBsb3QxIDwtIGdncGxvdChkYXRhLnNhcnMud29ybGQsIGFlcyh4PWRhdGUsIHk9Y3VycmVudC5jb25maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdDdXJyZW50IENvbmZpcm1lZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PW5ldy5jb25maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdEYWlseSBOZXcgQ29uZmlybWVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KIyMgc2hvdyB0d28gcGxvdHMgc2lkZSBieSBzaWRlDQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBuY29sPTIpDQpgYGANCmBgYHtyfQ0KIyMgYSBzY2F0dGVyIHBsb3Qgd2l0aCBhIHNtb290aGVkIGxpbmUgYW5kIHZlcnRpY2FsIHgtYXhpcyBsYWJlbHMNCnBsb3QxIDwtIGdncGxvdChkYXRhLnNhcnMud29ybGQsIGFlcyh4PWRhdGUsIHk9ZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIERlYXRocycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIGdncGxvdChkYXRhLnNhcnMud29ybGQsIGFlcyh4PWRhdGUsIHk9cmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QzIDwtIGdncGxvdChkYXRhLnNhcnMud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90NCA8LSBnZ3Bsb3QoZGF0YS5zYXJzLndvcmxkLCBhZXMoeD1kYXRlLCB5PW5ldy5yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdOZXcgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KIyMgc2hvdyBmb3VyIHBsb3RzIHRvZ2V0aGVyLCB3aXRoIDIgcGxvdHMgaW4gZWFjaCByb3cNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIHBsb3QzLCBwbG90NCwgbnJvdz0yKQ0KYGBgDQoNCmBgYHtyfQ0KI2RmX3RoYWkgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWRfdGhhaWxhbmRfdXBkYXRlZCIpKQ0KI2RmX3RoYWkgPC0gYXMuZGF0YS5mcmFtZShkZl90aGFpKQ0KDQpkZl90aGFpIDwtIHJlYWRfY3N2KCJEOi80RC0yL1Byb2plY3QgMi9EYXRhL2NvdmlkX3RoYWlsYW5kX3VwZGF0ZWQuY3N2IikNClN5cy5zZXRsb2NhbGUoIkxDX0NUWVBFIiwgIlRoYWkiKQ0Kb3B0aW9ucyhlbmNvZGluZyA9ICJVVEYtOCIpDQojVmlldyhkZl90aGFpKQ0KDQpkYXRlcy50aCA8LSBkZl90aGFpWywyXSU+JSBtZHkoKQ0KcmFuZ2UoZGF0ZXMudGgpDQptaW4uZGF0ZS50aCA8LSBtaW4oZGF0ZXMudGgpDQptYXguZGF0ZS50aCA8LSBtYXgoZGF0ZXMudGgpDQptaW4uZGF0ZS50eHQudGggPC0gbWluLmRhdGUudGggJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0LnRoIDwtIG1heC5kYXRlLnRoICU+JSBmb3JtYXQoJyVkICViICVZJykNCg0KZGZfdGhhaSRhbm5vdW5jZV9kYXRlIDwtIG1keShkZl90aGFpJGFubm91bmNlX2RhdGUpDQpkZl90aGFpJG5vdGlmaWNhdGlvbl9kYXRlIDwtIG1keShkZl90aGFpJG5vdGlmaWNhdGlvbl9kYXRlKQ0KZGZfdGhhaQ0KDQpkZl90aGFpIDwtIGRmX3RoYWkgJT4lIHNlbGVjdCghTm8uKSAlPiUgDQogIGdyb3VwX2J5KGFubm91bmNlX2RhdGUpDQpkZl90aGFpDQpgYGANCmBgYHtyfQ0KIyBUb3RhbCBjb25maXJtZWQgY2FzZXMgaW4gVGhhaWxhbmQNCmRhdGEudGhhaS5jb3VudCA8LSBkZl90aGFpICU+JQ0KICBzZWxlY3QoYW5ub3VuY2VfZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShjb21maXJtZWQgPSBuKCkpICAlPiUgYXMuZGF0YS5mcmFtZSgpDQpkYXRhLnRoYWkuY291bnQNCg0KZGF0YS50aGFpLmNvdW50JGN1bXVsYXRpdmVfY29uZmlybWVkIDwtIGN1bXN1bShkYXRhLnRoYWkuY291bnRbLCAyXSkNCmRhdGEudGhhaS5jb3VudCANCg0KZy50aCA8LSBnZ3Bsb3QoZGF0YS50aGFpLmNvdW50KSArIA0KICBnZW9tX2xpbmUoYWVzKHggPSBhbm5vdW5jZV9kYXRlLCB5ID0gY3VtdWxhdGl2ZV9jb25maXJtZWQpKSArDQogIGxhYnModGl0bGUgPSAiVGhhaSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjEpIikgKw0KICB4bGFiKCJEYXRlIChBbm5vdW5jZSBEYXRlKSIpICsgDQogIHlsYWIoIkNvbmZpcm1lZCBDYXNlcyIpDQoNCmcudGgNCg0KZ2x5LnRoIDwtIGdncGxvdGx5KGcudGgpDQpnbHkudGgNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgc2V4IChnZW5kZXIpDQpkYXRhLnRoYWkuZ2VuZGVyIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KHNleCkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gKGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkgJT4lIHJvdW5kKDIpKSAlPiUNCiAgI211dGF0ZShwb3MgPSBjdW1zdW0ocGVyY2VudCkgLSAwLjUqcGVyY2VudCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwZXJjZW50KSkNCmRhdGEudGhhaS5nZW5kZXINCg0KZGF0YS50aGFpLmdlbmRlciRzZXggPC0gZmFjdG9yKGRhdGEudGhhaS5nZW5kZXIkc2V4LCBsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoZGF0YS50aGFpLmdlbmRlciRzZXgpKQ0KZGF0YS50aGFpLmdlbmRlciRzZXgNCg0KZy50aC5nZW5kZXIgPC0gZGF0YS50aGFpLmdlbmRlciAlPiUgDQogIGdncGxvdChhZXMoeCA9ICIiLCB5ID0gcGVyY2VudCwgZmlsbCA9IHNleCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcigieSIpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50LCAiJSIpKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSANCg0KZy50aC5nZW5kZXINCg0KYGBgDQoNCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSBhZ2UNCmRhdGEudGhhaS5hZ2UgPC0gZGZfdGhhaSAlPiUNCiAgZ3JvdXBfYnkoYWdlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGNvdW50KSkNCmRhdGEudGhhaS5hZ2UNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgbmF0aW9uYWxpdHkNCmRhdGEudGhhaS5uYXRpb25hbGl0eSA8LSBkZl90aGFpICU+JQ0KICBncm91cF9ieShuYXRpb25hbGl0eSkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpDQpkYXRhLnRoYWkubmF0aW9uYWxpdHkNCmBgYA0KDQpgYGB7cn0NCiMgRGF0YSBpbiBVUw0KZGZfdXMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWR1cyIpKQ0KZGZfdXMgPC0gYXMuZGF0YS5mcmFtZShkZl91cykNCg0KZGZfdXNfcG9wIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfcG9wdWxhdGlvbiIpKSANCmRmX3VzX3BvcCA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX3BvcCkNCg0KZGZfdXNfZ2VuZGVyIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfZ2VuZGVyIikpIA0KZGZfdXNfZ2VuZGVyIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfZ2VuZGVyKQ0KDQpkZl91c19ldGhuaWMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9ldGhuaWMiKSkgDQpkZl91c19ldGhuaWMgPC0gYXMuZGF0YS5mcmFtZShkZl91c19ldGhuaWMpDQoNCmRmX3VzX2xvY2tkb3duIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfbG9ja2Rvd24iKSkgDQpkZl91c19sb2NrZG93biA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX2xvY2tkb3duKQ0KDQpkZl91c19oZWFsdGggPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9oZWFsdGgiKSkgDQpkZl91c19oZWFsdGggPC0gYXMuZGF0YS5mcmFtZShkZl91c19oZWFsdGgpDQoNCmRmX3VzX3Rlc3RpbmcgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV90ZXN0aW5nIikpIA0KZGZfdXNfdGVzdGluZyA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX3Rlc3RpbmcpDQoNCmRmX3VzX2xhdGxvbmcgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gdXNfbGF0bG9uZyIpKQ0KZGZfdXNfbGF0bG9uZyA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX2xhdGxvbmcpDQoNCiNWaWV3KGRmX3VzX2xhdGxvbmcpDQoNCmRhdGEudXMgPC0gZGZfdXMgJT4lIHNlbGVjdChkYXRlLCBzdGF0ZSwgY2FzZXMsIGRlYXRocykgJT4lDQogIG11dGF0ZShkYXRlID0gZGF0ZSAlPiUgbWR5KCkpICU+JQ0KICByZW5hbWUoImNvbmZpcm1lZCIgPSAiY2FzZXMiKQ0KI2RhdGEudXMNCg0KZGF0ZXMudXMgPC0gZGF0YS51c1ssMV0NCnJhbmdlKGRhdGVzLnVzKQ0KbWluLmRhdGUudXMgPC0gbWluKGRhdGVzLnVzKQ0KbWF4LmRhdGUudXMgPC0gbWF4KGRhdGVzLnVzKQ0KbWluLmRhdGUudHh0LnVzIDwtIG1pbi5kYXRlLnVzICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dC51cyA8LSBtYXguZGF0ZS51cyAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpkYXkxLnVzIDwtIG1pbihkYXRhLnVzJGRhdGUpDQoNCmRhdGEudXMudG90YWwgPC0gZGF0YS51cyAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShzdGF0ZT0nVVMnLA0KICAgICAgICAgICAgY29uZmlybWVkID0gc3VtKGNvbmZpcm1lZCwgbmEucm09VCksDQogICAgICAgICAgICBkZWF0aHMgPSBzdW0oZGVhdGhzLCBuYS5ybT1UKSkNCiNWaWV3KGRhdGEudXMudG90YWwpDQpkYXRhLnVzICU8PiUgcmJpbmQoZGF0YS51cy50b3RhbCkNClZpZXcoZGF0YS51cykNCg0KZGF0YS51cy5sb25nIDwtIGRhdGEudXMgJT4lIA0KICBnYXRoZXIoa2V5ID0gdHlwZSwgdmFsdWUgPSBjb3VudCwgLWMoZGF0ZSwgc3RhdGUpKSANCiNkYXRhLnVzLmxvbmcNCg0KdXMudG90YWwgPC0gZGF0YS51cy50b3RhbCAlPiUNCiAgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCAwLCBjb25maXJtZWQgLSBsYWcoY29uZmlybWVkLCBuPTEpKSwNCiAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgMCwgZGVhdGhzIC0gbGFnKGRlYXRocywgbj0xKSkpDQoNCnVzLnRvdGFsICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UobmV3LmNvbmZpcm1lZCA8IDAsIDAsIG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKG5ldy5kZWF0aHMgPCAwLCAwLCBuZXcuZGVhdGhzKSkNCnVzLnRvdGFsDQoNCnVzLnRvdGFsLmxvbmcgPC0gdXMudG90YWwgJT4lIA0KICBnYXRoZXIoa2V5ID0gdHlwZSwgdmFsdWUgPSBjb3VudCwgLWMoZGF0ZSwgc3RhdGUpKQ0KDQpWaWV3KHVzLnRvdGFsLmxvbmcpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KZzEgPC0gZGF0YS51cy5sb25nICU+JSBmaWx0ZXIoc3RhdGUgPT0gIlVTIikgJT4lDQogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBjb3VudCkpICsNCiAgZ2VvbV9hcmVhKGFlcyhmaWxsPXR5cGUpLCBhbHBoYT0wLjUpICsNCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiQ3VtdWxhdGl2ZSBjYXNlcyBpbiBVUyA6ICIsIG1pbi5kYXRlLnR4dC51cywgJy0nLCBtYXguZGF0ZS50eHQudXMsICIgKExvZyBTY2FsZSkiKSkgICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygncmVkJywgJ2JsYWNrJykpKw0KICB5bGFiKCIiKQ0KDQpnMiA8LSB1cy50b3RhbC5sb25nICU+JQ0KICBmaWx0ZXIodHlwZSAlaW4lIGMoIm5ldy5jb25maXJtZWQiKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBjb3VudCwgY29sb3IgPSB0eXBlKSkgKyANCiAgZ2VvbV9saW5lKCkgKyANCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiRGFpbHkgY29uZmlybWVkIGNhc2VzIGluIFVTIDogIiwgbWluLmRhdGUudHh0LnVzLCAnLScsIG1heC5kYXRlLnR4dC51cykpICsNCiAgeGxhYigiRGF0ZSIpICsNCiAgeWxhYigiQ29uZmlybWVkIGNhc2VzIikNCg0KZ2x5LnVzMSA8LSBnZ3Bsb3RseShnMSkNCmdseS51czIgPC0gZ2dwbG90bHkoZzIpDQoNCmdseS51czENCmdseS51czINCg0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KZGF0YS51cyAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIGNvbmZpcm1lZCAtIGxhZyhjb25maXJtZWQsIG49MSkpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKGRhdGUgPT0gZGF5MSwgTkEsIGRlYXRocyAtIGxhZyhkZWF0aHMsIG49MSkpKQ0KDQpkYXRhLnVzICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UobmV3LmNvbmZpcm1lZCA8IDAsIDAsIG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKG5ldy5kZWF0aHMgPCAwLCAwLCBuZXcuZGVhdGhzKSkNClZpZXcoZGF0YS51cykNCg0KI2RhdGEudXMuZGFpbHkgPC0gZGF0YS51cyAlPiUgZmlsdGVyKHN0YXRlID09ICJVUyIpDQojVmlldyhkYXRhLnVzLmRhaWx5KQ0KDQpkYXRhLnVzLnBvcCA8LSBkZl91c19wb3AgJT4lIHNlbGVjdChTdGF0ZSwgUG9wdWxhdGlvbikgJT4lDQogIHJlbmFtZSgic3RhdGUiID0gIlN0YXRlIikgDQojZGF0YS51cy5wb3ANCg0KZGF0YS51cy5nZW5kZXIgPC0gZGZfdXNfZ2VuZGVyICU+JSBzZWxlY3QoU3RhdGUsIE1hbGUsIEZlbWFsZSkgJT4lDQogIHJlbmFtZSgic3RhdGUiID0gIlN0YXRlIikgDQpkYXRhLnVzLmdlbmRlcg0KDQpkYXRhLnVzLmxhdGVzdCA8LSBkYXRhLnVzICU+JQ0KICBmaWx0ZXIoZGF0ZSA9PSBtYXguZGF0ZS51cykgJT4lIA0KICBtZXJnZShkYXRhLnVzLnBvcCwgYnkgPSAic3RhdGUiLCBhbGwueCA9IFQpICU+JQ0KICBtZXJnZShkYXRhLnVzLmdlbmRlciwgYnkgPSAic3RhdGUiLCBhbGwueCA9IFQpDQojVmlldyhkYXRhLnVzLmxhdGVzdCkNCg0KZGF0YS51cy5sYXRlc3QkUG9wdWxhdGlvbltkYXRhLnVzLmxhdGVzdCRzdGF0ZSA9PSAiVVMiXSA8LSBzdW0oZGF0YS51cy5wb3AkUG9wdWxhdGlvbikNCmRhdGEudXMubGF0ZXN0ICU8PiUgbXV0YXRlKHJhbmtpbmcgPSBkZW5zZV9yYW5rKGRlc2MoY29uZmlybWVkKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb25maXJtZWQucmF0ZSA9ICgxMDAgKiBjb25maXJtZWQgLyBQb3B1bGF0aW9uKSAlPiUgcm91bmQoMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aC5yYXRlID0gKDEwMCAqIGRlYXRocyAvIGNvbmZpcm1lZCkgJT4lIHJvdW5kKDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgTWFsZS5jb25maXJtZWQgPSAoTWFsZSAqIGNvbmZpcm1lZCkgJT4lIHJvdW5kKDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgRmVtYWxlLmNvbmZpcm1lZCA9IEZlbWFsZSAqIGNvbmZpcm1lZCAlPiUgcm91bmQoMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBNYWxlLmRlYXRocyA9IE1hbGUgKiBkZWF0aHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGZW1hbGUuZGVhdGhzID0gRmVtYWxlICogZGVhdGhzKSAlPiUNCiAgYXJyYW5nZShyYW5raW5nKSANCg0KdG9wLnVzIDwtIGRhdGEudXMubGF0ZXN0WywxXQ0KdG9wLnVzDQoNCg0KZGF0YS51cy5sYXRlc3QNCg0KDQpgYGANCg0KYGBge3J9DQojIExpc3Qgb2YgdG9wIDIwIHN0YXRlDQprIDwtIDIwDQpkYXRhLnVzLnRvcCA8LSBkYXRhLnVzLmxhdGVzdCAlPiUNCiAgZmlsdGVyKHJhbmtpbmcgPD0gaysxKSAlPiUgDQogIGFycmFuZ2UocmFua2luZykgDQoNCnVzLnN0YXRlLnRvcCA8LSBkYXRhLnVzLnRvcCAlPiUgcHVsbChzdGF0ZSkgJT4lIGFzLmNoYXJhY3RlcigpDQp1cy5zdGF0ZS50b3AgICU+JSBzZXRkaWZmKCdVUycpICU+JSBwcmludCgpDQoNCiMgY29uZmlybWVkIHJhdGUgJiBkZWF0aCByYXRlIG9mIHRvcCAyMCBzdGF0ZQ0KZy5yYXRlIDwtIGRhdGEudXMubGF0ZXN0ICU+JSBmaWx0ZXIoc3RhdGUgJWluJSB1cy5zdGF0ZS50b3AgJiBzdGF0ZSAhPSAiVVMiKSAlPiUNCiAgc2VsZWN0KHN0YXRlLCBjb25maXJtZWQucmF0ZSwgZGVhdGgucmF0ZSwgcmFua2luZykgJT4lDQogIGdhdGhlcihrZXkgPSBUeXBlLCB2YWx1ZSA9IFBlcmNlbnQsIC1jKHN0YXRlLCByYW5raW5nKSkgJT4lDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHN0YXRlLCAtZGVzYyhyYW5raW5nKSksIHk9UGVyY2VudCwgZmlsbCA9IFBlcmNlbnQpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjZWJiYzYyIiwgaGlnaCA9ICIjYjQyMDA2IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPVBlcmNlbnQsIHk9UGVyY2VudCksIHNpemU9Mywgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ0NvbmZpcm1lZCBSYXRlICYgRGVhdGggUmF0ZSBvZiBUb3AgMjAgU3RhdGUgaW4gVVMnKSkgKw0KICAjc2NhbGVfZmlsbF9jb250aW51b3VzKG5hbWU9J1N0YXRlJywgbGFiZWxzPWFlcyhQZXJjZW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAoflR5cGUsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKSANCg0KZy5yYXRlDQpgYGANCg0KDQpgYGB7cn0NCnVzLmNvbmZpcm1lZC5udW0gPC0gZGF0YS51cy50b3AkY29uZmlybWVkW2RhdGEudXMudG9wJHN0YXRlID09ICJVUyJdICU+JSBhcy5udW1lcmljKCkNCnVzLmNvbmZpcm1lZC5udW0NCg0KZGF0YS51cy50b3AgJTw+JSBtdXRhdGUoY29uZmlybWVkLnBlci51cyA9IChjb25maXJtZWQgKiAxMDAgLyB1cy5jb25maXJtZWQubnVtKSAlPiUgcm91bmQoMSkpDQpkYXRhLnVzLnRvcA0KDQpnLnVzLnRvcDEgPC0gZGF0YS51cy50b3AgJT4lDQogIGZpbHRlcihzdGF0ZSAhPSAiVVMiKSAlPiUNCiAgc2VsZWN0KHN0YXRlLCBjb25maXJtZWQsIGNvbmZpcm1lZC5wZXIudXMsIHJhbmtpbmcpICU+JQ0KICBnYXRoZXIoa2V5ID0gVHlwZSwgdmFsdWUgPSBjb3VudCwgLWMoc3RhdGUsIHJhbmtpbmcsIGNvbmZpcm1lZC5wZXIudXMpKSAlPiUNCiAgDQogIGdncGxvdChhZXMoZmlsbCA9IGNvdW50LCB5ID0gY291bnQsIHggPSByZW9yZGVyKHN0YXRlLCAtZGVzYyhyYW5raW5nKSkpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiMjAgc3RhdGUgaW4gVVMgd2l0aCBtb3N0IGNvbmZpcm1lZCBjYXNlcyIpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI2ViYmM2MiIsIGhpZ2ggPSAiI2I0MjAwNiIpICsNCiAgeGxhYigiIikgKyANCiAgeWxhYigiQ29uZmlybWVkIENhc2VzIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlMChjb25maXJtZWQucGVyLnVzLCAiJSIpKSwgc2l6ZT0zLCB2anVzdD0tMC41KSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsdmp1c3QgPSAwLjUpKSsNCiAgdGhlbWUoDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICNsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KDQpnLnVzLnRvcDENCg0KZ2x5LnVzLnRvcDEgPC0gZ2dwbG90bHkoZy51cy50b3AxKQ0KZ2x5LnVzLnRvcDENCg0KYGBgDQoNCmBgYHtyfQ0KZGF0YS51cy5sYXRsb25nIDwtIGRmX3VzX2xhdGxvbmcgJT4lDQogIHJlbmFtZSgibGF0IiA9ICJsYXRpdHVkZSIsICJsb25nIiA9ICJsb25naXR1ZGUiKQ0KDQojbGFiZWxfd29ybGQgPC0gbGF0LmxvbmcgDQojbGFiZWxfd29ybGQkYXZnX3RlbXAgPC0gYXMubnVtZXJpYyhsYWJlbF93b3JsZFssIG5hbWVzKGxhYmVsX3dvcmxkKSAlaW4lIGMoImF2Z190ZW1wIildKQ0KI2xhYmVsX3dvcmxkIDwtIGxhYmVsX3dvcmxkICU+JSAgDQojICBtdXRhdGUodHh0PXBhc3RlMCgnPGI+JyxyYW5raW5nLCAnPC9iPicsDQojICAgICAgICAgICAgICAgICAgICAnPGJyLz4nLCc8Yj4nLGNvdW50cnksICc8L2I+JywNCiMgICAgICAgICAgICAgICAgICAgICc8YnIvPicsICJUZW1wZXJhdHVyZTogICIsYXZnX3RlbXAsICcgwrBDJywNCiMgICAgICAgICAgICAgICAgICAgICc8YnIvPicsICJDb25maXJtZWQ6ICAiLCBjb25maXJtZWQsIA0KIyAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIkRlYXRoczogIiwgZGVhdGhzLA0KIyAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIlJlY292ZXJlZDogIiwgcmVjb3ZlcmVkDQojICAgICAgICAgICAgICAgICAgICApKSANCg0KI2xhYmVsX3dvcmxkJHR4dCA8LSBsYWJlbF93b3JsZCR0eHQgJT4lIGxhcHBseShodG1sdG9vbHM6OkhUTUwpDQojbGFiZWxfd29ybGQNCg0KbWFwLnVzIDwtIGRhdGEudXMubGF0ZXN0ICU+JQ0KICBzZWxlY3Qoc3RhdGUsIGNvbmZpcm1lZCwgY29uZmlybWVkLnJhdGUsIGRlYXRocywgZGVhdGgucmF0ZSwgcmFua2luZykgJT4lDQogIG1lcmdlKGRhdGEudXMubGF0bG9uZykgJT4lDQogIG11dGF0ZSgpDQptYXAudXMNCmBgYA0KDQoNCg0KYGBge3J9DQptZXJnY291bnRyeSA9IGZ1bmN0aW9uKGRhdGExLGRhdGEyKXsNCiAgZGF0YSA8LSBtZXJnZSh4ID0gZGF0YTEsIHkgPSBkYXRhMiwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQogIHJldHVybihkYXRhKQ0KfQ0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLCB5ID0gZGZfZ2RwMjAxOSwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lIA0KICBzZWxlY3QoLWMoY29kZSxyYW5rLG5ldy5jb25maXJtZWQsbmV3LmRlYXRocyxjdXJyZW50LmNvbmZpcm1lZCxwb3B1bGF0aW9uKSkgJT4lIA0KICByZW5hbWUoR0RQPSJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLndvcmxkLCB5ID0gZGZfaGVhbHQsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpICU+JQ0KICByZW5hbWUoaGVhbHRoY2FyZT0iaGVhbHRoQ2FyZUluZGV4IikNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLndvcmxkLCB5ID0gZGZfcG9ybmh1YiwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lDQogIHJlbmFtZShQb3JuaHViID0gIlBvcm5odWJJbmRleCglKSIpDQoNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdjb3VudHJ5KGRhdGEudG9wLndvcmxkLCBkZl90ZW1wKQ0KaW5kZXggPC0gaXMubmEoZGF0YS50b3Aud29ybGQpDQpkYXRhLnRvcC53b3JsZFtpbmRleF0gPC0gMA0KZGF0YS50b3Aud29ybGQNCiNWaWV3KGRhdGEudG9wLndvcmxkKQ0KDQpub3JtYWxpemUgPSBmdW5jdGlvbihkYXRhKXsNCiAgI3JldHVybiAoKGRhdGEgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKS8obWF4KGRhdGEsbmEucm0gPSBUUlVFKSAtIG1pbihkYXRhLG5hLnJtID0gVFJVRSkpKQ0KICB6IDwtIHNjYWxlKGRhdGEpOw0KICB0YW5oKHovMikNCn0NCg0Kbm9ybV9kYXRhID0gYXMuZGF0YS5mcmFtZShhcHBseShkYXRhLnRvcC53b3JsZFssMjoxMl0sMixub3JtYWxpemUpKQ0KY29ycl9kYXRhIDwtIG5vcm1fZGF0YQ0Kbm9ybV9kYXRhJGNvdW50cnkgPC0gYygiQXJnZW50aW5hIiwiQmFuZ2xhZGVzaCIsIkJyYXppbCIsIkNoaWxlIiwiQ29sb21iaWEiLCJGcmFuY2UiLCJHZXJtYW55IiwiSW5kaWEiLCJJcmFuIiwiSXRhbHkiLCJNZXhpY28iLCJQYWtpc3RhbiIsIlBlcnUiLCJSdXNzaWEiLCJzYXVkaSBBcmFiaWEiLCJTb3V0aCBBZnJpY2EiLCJTcGFpbiIsIlR1cmtleSIsIlVuaXRlZCBLaW5nZG9tIiwiVVMiKQ0KVmlldyhub3JtX2RhdGEpDQpub3JtX2RhdGFfcGxvdCA8LSBzZWxlY3Qobm9ybV9kYXRhLCJjb3VudHJ5IiwiY29uZmlybS5yYXRlIiwiZGVhdGgucmF0ZSIsInJlY292ZXIucmF0ZSIsImhlYWx0aGNhcmUiLCJQb3JuaHViIiwiR0RQIiwiYXZnX3RlbXAiKQ0Kbm9ybV9kYXRhX3Bsb3QgJTw+JSBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5KSkNCmxldmVsX29yZGVyIDwtIGZhY3Rvcihub3JtX2RhdGFfcGxvdCR0eXBlLCANCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbCA9IGMoIkdEUCIsImF2Z190ZW1wIiwiaGVhbHRoY2FyZSIsInJlY292ZXIucmF0ZSIsImRlYXRoLnJhdGUiLCJjb25maXJtLnJhdGUiLCJQb3JuaHViIikpDQpnZ3Bsb3QoZGF0YSA9IG5vcm1fZGF0YV9wbG90LCBhZXMoeD1jb3VudHJ5LCB5PWxldmVsX29yZGVyLCBmaWxsPWNvdW50KSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJwaW5rIiwgaGlnaCA9ICJibHVlIikgKw0KICB4bGFiKCIiKSArDQogIHlsYWIoIiIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsdmp1c3QgPSAxKSkrDQogIHRoZW1lKA0KICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICkNCmBgYA0KDQoNCmBgYHtyfQ0KI2NvcnJlbGF0aW9uDQpjb3JyX2RhdGEgJTw+JSBzZWxlY3QoYyhHRFAsY29uZmlybS5yYXRlLGRlYXRoLnJhdGUscmVjb3Zlci5yYXRlLCBoZWFsdGhjYXJlLCBhdmdfdGVtcCwgUG9ybmh1YikpDQpoZWFkKGNvcnJfZGF0YSkNCmNvcihjb3JyX2RhdGEpDQpnZ2NvcnJwbG90KGNvcihjb3JyX2RhdGEpLGhjLm9yZGVyID0gVFJVRSwNCiAgICAgICAgICAgb3V0bGluZS5jb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgIGNvbG9ycyA9IGMoIiM2RDlFQzEiLCJ3aGl0ZSIsIiNFNDY3MjYiKSwNCiAgICAgICAgICAgbGFiID0gVFJVRSkNCmBgYA0KDQoNCg0K